diff -Nru sbws-1.0.2/CHANGELOG.md sbws-1.1.0/CHANGELOG.md --- sbws-1.0.2/CHANGELOG.md 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/CHANGELOG.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,288 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [1.0.2] - 2018-11-10 - -### Fixed - -- Update bandwidth file specification version in the `generator` (#28366). -- Use 5 "=" characters as terminator in the bandwidth files (#28379) - -### Changed - -- Include the headers about eligible relays in all the bandwidth files, - not only in the ones that does not have enough eligible relays (#28365). - -## [1.0.1] - 2018-11-01 - -### Changed - -- Change default directories when sbws is run from a system service (#28268). - -## [1.0.0] - 2018-10-29 - -**Important changes**: - -- `generate` includes extra statistics header lines when the number of - eligible relays to include is less than the 60% of the network. - It does not include the relays' lines. -- Speed up `scanner` by disabling RTT measurements and waiting for - measurement threads before prioritizing again the list of relays to measure. - -### Fixed - -- Update python minimal version in setup (#28043) -- Catch unhandled exception when we fail to resolve a domain name (#28141) -- Bandwidth filtered is the maximum between the bandwidth measurements and - their mean, not the minimum (#28215) -- Stop measuring the same relay by two threads(#28061) - -### Changed - -- Move ``examples/`` to ``docs/`` (#28040) -- Number of results comparison and number of results away from each other are - incorrect (#28041) -- Stop removing results that are not away from some other X secs (#28103) -- Use secs-away when provided instead of data_period (#28105) -- Disable measuring RTTs (#28159) -- Rename bandwidth file keyvalues (#28197) - -## Added - -- Write bw file only when the percentage of measured relays is bigger than 60% - (#28062) -- When the percentage of measured relays is less than the 60%, do not include - the relays in the bandwidth file and instead include some statistics in the - header (#28076) -- When the percentage of measured relays is less than the 60% and it was more - before, warn about it (#28155) -- When the difference between the total consensus bandwidth and the total - in the bandwidth lines is larger than 50%, warn (#28216) -- Add documentation about how the bandwidth measurements are selected and - scaled before writing them to the Bandwidth File (#27692) - -## [0.8.0] - 2018-10-08 - -**Important changes**: - -- Implement Torflow scaling/aggregation to be able to substitute Torflow with - sbws without affecting the bandwidth files results. -- Change stem dependency to 1.7.0, which removes the need for `dependency_links`` -- Update and cleanup documentation - -### Added - -- Add system physical requirements section to INSTALL (#26937) -- Warn when there is not enough disk space (#26937) -- Implement Torflow scaling (#27108) -- Create methods to easy graph generation and obtain statistics - to compare with current torflow results.(#27688) -- Implement rounding bw in bandwidth files to 2 insignificant digits(#27337) -- Filter results in order to include relays in the bandwidth file that:(#27338) - - have at least two measured bandwidths - - the measured bandwidths are within 24 hours of each other - - have at least two descriptor observed bandwidths - - the descriptor observed bandwidths are within 24 hours of each other - -### Fixed - -- Broken environment variable in default sbws config. To use envvar $FOO, write - $$FOO in the config. -- Stop using directory as argument in integration tests (#27342) -- Fix typo getting configuration option to allow logging to file (#27960) -- Set int type to new arguments that otherwise would be string (#27918) -- Stop printing arguments default values, since they are printed by default - (#27916) -- Use dash instead of underscore in new cli argument names (#27917) - -### Changed - -- sbws install doc is confusing (#27341) - - Include system and Python dependencies in `INSTALL`. - - Include dependencies for docs and tests in `INSTALL`. - - Point to `DEPLOY` to run sbws. - - Remove obsolete sections in `INSTALL` - - Simplify `DEPLOY`, reuse terms in the `glossary`. - - Remove obsolete ``sbws init`` from `DEPLOY`. - - Point to config documentation. - - Add, unify and reuse terms in `glossary`. -- refactor v3bwfile (#27386): move scaling method inside class -- use custom ``install_command`` to test installation commands while - ``dependency_links`` is needed until #26914 is fixed. (#27704) -- documentation cleanup (#27773) - - split, merge, simplify, extend, reorganize sections and files -- generate scales as Torflow by default (#27976) -- Replace stem ``dependency_links`` by stem 1.7.0 (#27705). This also eliminates - the need for custom ``install_command`` in tox. - -## [0.7.0] - 2018-08-09 - -**Important changes**: - -- `cleanup/stale_days` is renamed to `cleanup/data_files_compress_after_days` -- `cleanup/rotten_days` is renamed to `cleanup/data_files_delete_after_days` -- sbws now takes as an argument the path to a config file (which contains - `sbws_home`) instead of `sbws_home` (which contains the path to a config -file) - -### Added - -- Log line on start up with sbws version, platform info, and library versions -(trac#26751) -- Manual pages (#26926) - -### Fixed - -- Stop deleting the latest.v3bw symlink. Instead, do an atomic rename. - (#26740) -- State file for storing the last time `sbws scanner` was started, and able to - be used for storing many other types of state in the future. (GH#166) -- Log files weren't rotating. Now they are. (#26881) - -### Changed - -- Remove test data v3bw file and generate it from the same test. (#26736) -- Stop using food terms for cleanup-related config options -- Cleanup command now cleans up old v3bw files too (#26701) -- Make sbws more compatible with system packages: (#26862) - - Allow a configuration file argument - - Remove directory argument - - Create minimal user configuration when running - - Do not require to run a command to initialize - - Initialize directories when running - - Do not require configuration file inside directories specified by the - configuration - -## [0.6.0] - 2018-07-11 - -**Important changes**: - -- The way users configure logging has changed. No longer are most users - expected to be familiar with how to configure python's standard logging -library with a config file. Instead we've abstracted out the setting of log -level, format, and destinations to make these settings more accessible to -users. Expert users familiar with [the logging config file format][logconffmt] -can still make tweaks. - -Summary of changes: - -- Make logging configuration easier for the user. -- Add UML diagrams to documentation. They can be found in docs/source/images/ - and regenerated with `make umlsvg` in docs/. - -[logconffmt]: https://docs.python.org/3/library/logging.config.html#logging-config-fileformat - -### Added - -- UML diagrams to documentation. In docs/ run `make umlsvg` to rebuild them. - Requires graphviz to be installed.(GHPR#226) -- Add metadata to setup.py, useful for source/binary distributions. -- Add possibility to log to system log. (#26683) -- Add option to cleanup v3bw files. (#26701) - -### Fixed - -- Measure relays that have both Exit and BadExit as non-exits, which is how - clients would use them. (GH#217) -- Could not init sbws because of a catch-22 related to logging configuration. - Overhaul how logging is configured. (GH#186 GHPR#224) -- Call write method of V3BWFile class from the object instance. (#26671) -- Stop calculating median on empty list .(#26666) - -### Changed - -- Remove is_controller_ok. Instead catch possible controller exceptions and -log them - -### Removed - -- Two parsing/plotting scripts in scripts/tools/ that can now be found at - - -## [0.5.0] - 2018-06-26 - -**Important changes**: - -- Result format changed, causing a version bump to 4. Updating sbws to 0.5.0 - will cause it to ignore results with version less than 4. - -Summary of changes: - -- Keep previously-generated v3bw files -- Allow a relay to limit its weight based on - RelayBandwidthRate/MaxAdvertisedBandwidth -- 1 CPU usage optimization -- 1 memory usage optimization - -### Added - -- Use a relay's {,Relay}BandwidthRate/MaxAdvertisedBandwidth as an upper bound - on the measurements we make for it. (GH#155) -- Ability to only consider results for a given relay valid if they came from - when that relay is using its most recent known IP address. Thanks Juga. -(GH#154 GHPR#199) -- Maintenance script to help us find functions that are (probably) no longer - being called. -- Integration test(s) for RelayPrioritizer (GHPR#206) -- Git/GitHub usage guidelines to CONTRIBUTING document (GH#208 GHPR#215) - -### Fixed - -- Make relay priority calculations take only ~5% of the time they used to (3s - vs 60s) by using sets instead of lists when selecting non-Authority relays. -(GH#204) -- Make relay list refreshing take much less time by not allowing worker threads - to dogpile on the CPU. Before they would all start requesting descriptors -from Tor at roughly the same time, causing us to overload our CPU core and make -the process take unnecessarily long. Now we let one thread do the work so it -can peg the CPU on its own and get the refresh done ASAP. -(GH#205) -- Catch a JSON decode exception on malformed results so sbws can continue - gracefully (GH#210 GHPR#212) - -### Changed - -- Change the path where the Bandwidth List files are generated: now they are - stored in `v3bw` directory, named `YYmmdd_HHMMSS.v3bw`, and previously -generated ones are kept. A `latest.v3bw` symlink is updated. (GH#179 GHPR#190) -- Code refactoring in the v3bw classes and generation area -- Replace v3bw-into-xy bash script with python script to handle a more complex - v3bw file format (GH#182) - -## [0.4.1] - 2018-06-14 - -### Changed - -- If the relay to measure is an exit, put it in the exit position and choose a - non-exit to help. Previously the relay to measure would always be the first -hop. (GH#181) -- Try harder to find a relay to help measure the target relay with two changes. - Essentially: (1) Instead of only picking from relays that are 1.25 - 2.00 -times faster than it by consensus weight, try (in order) to find a relay that -is at least 2.00, 1.75, 1.50, 1.25, or 1.00 times as fast. If that fails, -instead of giving up, (2) pick the fastest relay in the network instead of -giving up. This compliments the previous change about measuring target exits in -the exit position. - -### Fixed - -- Exception that causes sbws to fall back to one measurement thread. We first - tried fixing something in this area with `88fae60bc` but neglected to -remember that `.join()` wants only string arguments and can't handle a `None`. -So fix that. -- Exception when failing to get a relay's `ed25519_master_key` from Tor and - trying to do `.rstrip()` on a None. -- `earliest_bandwidth` being the newest bw not the oldest (thanks juga0) -- `node_id` was missing the character "$" at the beginning - -[Unreleased]: https://github.com/pastly/simple-bw-scanner/compare/v0.7.0...master -[0.7.0]: https://github.com/pastly/simple-bw-scanner/compare/v0.6.0...v0.7.0 -[0.6.0]: https://github.com/pastly/simple-bw-scanner/compare/v0.5.0...v0.6.0 -[0.5.0]: https://github.com/pastly/simple-bw-scanner/compare/v0.4.1...v0.5.0 -[0.4.1]: https://github.com/pastly/simple-bw-scanner/compare/v0.4.0...v0.4.1 diff -Nru sbws-1.0.2/CHANGELOG.rst sbws-1.1.0/CHANGELOG.rst --- sbws-1.0.2/CHANGELOG.rst 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/CHANGELOG.rst 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,459 @@ +Changelog +========= + +All notable changes to this project will be documented in this file. + +The format is based on `Keep a +Changelog `__ and this project +adheres to `Semantic Versioning `__. + +v1.1.0 (2019-03-27) +------------------- + +New +~~~ + +- V3bwfile: Report excluded relays. + Closes: #28565. +- V3bwfile: Add time to report half network. + Closes: #28983 +- Destination: Recover destination when it failed. + Closes: #29589. +- V3bwfile: Report relays that fail to be measured. + Closes: #28567. +- V3bwfile: Report relays that are not measured measured. + Closes: #28566 +- V3bwfile: Add KeyValues to monitor relays. + Closes: #29591. +- Docs: document that authorities are not measured. + Closes: #29722 +- Scanner: Warn when there is no progress. + Closes: #28652 + +Fix +~~~ +- v3bwfile: Report relays even when they don't reach a minimum number. + Closes: #29853. +- Minor fixes. Closes #29891. +- Relaylist: Convert consensus bandwidth to bytes. + + +v1.0.5 (2019-03-06) +------------------- + +- Release v1.0.5. + this time with the correct version + +v1.0.4 (2019-03-06) +------------------- + +- Release v1.0.4. + because there was a commit missing between `1.0.3` and `1.0.4-dev0` + and what is released as `1.0.3` has version `1.0.4-dev0` and it + can not be fixed now. + +v1.0.3 (2019-02-28) +------------------- + +Fixed +~~~~~~ + +- scanner: check that ResultDump queue is not full + Fixes bug #28866. Bugfix v0.1.0. +- config: set stdout log level to cli argument. Closes: #29199 +- cleanup: Use getpath to get configuration paths. Bugfix v0.7.0. +- destination: stop running twice usability tests. + Fixes bug #28897. Bugfix v0.3.0 +- globals, stem: explain where torrc options are. + Fixes bug #28646. Bugfix v0.4.0 +- stem: disable pad connections. Fixes bug 28692. Bugfix v0.4.0 +- generate: Load all results, including error ones. + Closes #29568. Bugfix v0.4.0 (line introduced in v0.1.0). +- relayprioritizer: Stop prioritizing relays that tend to fail. + Fixes bug #28868. Bugfix v0.1.0 +- circuitbuilder: Stop building the circuit 3 times. + Fixes bug #29295. Bugfix v0.1.0. +- docs: add verify option to man and example. + Closes bug #28788. Bugfix v0.4.0. +- CI: run scanner using the test network. Fixes bug #28933. Bugfix v0.1.0. +- scanner: catch SIGINT in the main loop. Fixes bug #28869. Bugfix v0.1.0. +- Stop including tests network as binary blob. Fixes bug #28590. Bugfix v0.4.0. +- relaylist: remove assertions that fail measurement. + Closes #28870. Bugfix v0.4.0 +- config: Use configuration provided as argument. + Fixes bug #28724. Bugfix v0.7.0. +- stem: parse torrc options that are only a key. + Fixes bug #28715. Bugfix v0.1.1 +- stem: Stop merging multiple torrc options with the same name. + Fixes bug #28738. Bugfix v0.1.1 +- docs: add note about syslog when running systemd. + Closes bug #28761. Bugfix v0.6.0 +- CI: include deb.torproject.org key. + Closes #28922. Bugfix v1.0.3-dev0 +- config: stop allowing http servers without tls. + Fixes bug #28789. Bugfix v0.2.0. +- Make info level logs more clear and consistent. + Closes bug #28736. Bugfix v0.3.0. +- CI: check broken links in the docs. Closes #28670. +- docs: add scanner and destination requirements. + Closes bug #28647. Bugfix v0.4.0 +- generate: use round_digs variable name in methods. + Closes bug #28602. Bugfix 1.0.3-dev0 +- docs: Change old broken links in the documentation. Closes #28662. +- docs: replace http by https in links. Closes #28661. +- Fix git repository link. Fixes bug #28762. Bugfix v1.0.0. +- docs: add example destination in DEPLOY. Closes #28649. +- docs: Change links to be interpreted by ReST. Closes #28648. +- Force rtfd.io to install the package. Closes bug #28601. +- config: continue when the file is not found. Closes: #28550. +- Stop resolving domains locally and check same flags for the 2nd hop. + Closes bug #28458, #28471. Bugfix 1.0.4. +- Limit the relays' bandwidth to their consensus bandwidth. Closes #28598. +- globals: add torrc logging options. Closes #28645. Bugfix v0.2.0. +- Limit bandwidth to the relay MaxAdvertisedBandwidth + Fixes bug #28588. Bugfix 0.8.0. +- Exclude results, then check for the minimum number. Closes bug 28572. +- Make sbws round to 3 significant figures in torflow rounding mode. + Bugfix on 27337 in sbws 1.0. Part of 28442. + +Changed +~~~~~~~~ + +- tests: remove unused testnets. Fixes bug #29046. Bugfix v0.4.0. +- scanner, destination: Log all possible exceptions. +- docs: Update/improve documentation on how the scanner/generator work. + Closes: #29149 +- Requests: Change make_session to use the TimedSession. +- CI: change to Ubuntu Xenial. +- docs: stop editing changelog on every bug/ticket. Closes ticket #28572. +- Change sbws scaling method to torflow. Closes: #28446. +- Round bandwidths to 2 significant digits by default. + Implements part of proposal 276. Implements 28451. + +Added +~~~~~~ + +- Send scanner metadata as part of every HTTP request. Closes: #28741 +- scanner: log backtrace when not progressing. Closes: 28932 + +v1.0.2 (2018-11-10) +------------------- + +Fixed +~~~~~ + +- Update bandwidth file specification version in the ``generator`` + (#28366). +- Use 5 "=" characters as terminator in the bandwidth files (#28379) + +Changed +~~~~~~~ + +- Include the headers about eligible relays in all the bandwidth files, + not only in the ones that does not have enough eligible relays + (#28365). + +v1.0.1 (2018-11-01) +------------------- + +Changed +~~~~~~~ + +- Change default directories when sbws is run from a system service + (#28268). + +v1.0.0 (2018-10-29) +------------------- + +**Important changes**: + +- ``generate`` includes extra statistics header lines when the number + of eligible relays to include is less than the 60% of the network. It + does not include the relays' lines. +- Speed up ``scanner`` by disabling RTT measurements and waiting for + measurement threads before prioritizing again the list of relays to + measure. + +Fixed +~~~~~ + +- Update python minimal version in setup (#28043) +- Catch unhandled exception when we fail to resolve a domain name + (#28141) +- Bandwidth filtered is the maximum between the bandwidth measurements + and their mean, not the minimum (#28215) +- Stop measuring the same relay by two threads(#28061) + +Changed +~~~~~~~ + +- Move ``examples/`` to ``docs/`` (#28040) +- Number of results comparison and number of results away from each + other are incorrect (#28041) +- Stop removing results that are not away from some other X secs + (#28103) +- Use secs-away when provided instead of data\_period (#28105) +- Disable measuring RTTs (#28159) +- Rename bandwidth file keyvalues (#28197) + +Added +----- + +- Write bw file only when the percentage of measured relays is bigger + than 60% (#28062) +- When the percentage of measured relays is less than the 60%, do not + include the relays in the bandwidth file and instead include some + statistics in the header (#28076) +- When the percentage of measured relays is less than the 60% and it + was more before, warn about it (#28155) +- When the difference between the total consensus bandwidth and the + total in the bandwidth lines is larger than 50%, warn (#28216) +- Add documentation about how the bandwidth measurements are selected + and scaled before writing them to the Bandwidth File (#27692) + +v0.8.0 (2018-10-08) +------------------- + +**Important changes**: + +- Implement Torflow scaling/aggregation to be able to substitute + Torflow with sbws without affecting the bandwidth files results. +- Change stem dependency to 1.7.0, which removes the need for + ``dependency_links`` +- Update and cleanup documentation + +Added +~~~~~ + +- Add system physical requirements section to INSTALL (#26937) +- Warn when there is not enough disk space (#26937) +- Implement Torflow scaling (#27108) +- Create methods to easy graph generation and obtain statistics to + compare with current torflow results.(#27688) +- Implement rounding bw in bandwidth files to 2 insignificant + digits(#27337) +- Filter results in order to include relays in the bandwidth file + that:(#27338) +- have at least two measured bandwidths +- the measured bandwidths are within 24 hours of each other +- have at least two descriptor observed bandwidths +- the descriptor observed bandwidths are within 24 hours of each other + +Fixed +~~~~~ + +- Broken environment variable in default sbws config. To use envvar + $FOO, write $$FOO in the config. +- Stop using directory as argument in integration tests (#27342) +- Fix typo getting configuration option to allow logging to file + (#27960) +- Set int type to new arguments that otherwise would be string (#27918) +- Stop printing arguments default values, since they are printed by + default (#27916) +- Use dash instead of underscore in new cli argument names (#27917) + +Changed +~~~~~~~ + +- sbws install doc is confusing (#27341) +- Include system and Python dependencies in ``INSTALL``. +- Include dependencies for docs and tests in ``INSTALL``. +- Point to ``DEPLOY`` to run sbws. +- Remove obsolete sections in ``INSTALL`` +- Simplify ``DEPLOY``, reuse terms in the ``glossary``. +- Remove obsolete ``sbws init`` from ``DEPLOY``. +- Point to config documentation. +- Add, unify and reuse terms in ``glossary``. +- refactor v3bwfile (#27386): move scaling method inside class +- use custom ``install_command`` to test installation commands while + ``dependency_links`` is needed until #26914 is fixed. (#27704) +- documentation cleanup (#27773) +- split, merge, simplify, extend, reorganize sections and files +- generate scales as Torflow by default (#27976) +- Replace stem ``dependency_links`` by stem 1.7.0 (#27705). This also + eliminates the need for custom ``install_command`` in tox. + +v0.7.0 (2018-08-09) +------------------- + +**Important changes**: + +- ``cleanup/stale_days`` is renamed to + ``cleanup/data_files_compress_after_days`` +- ``cleanup/rotten_days`` is renamed to + ``cleanup/data_files_delete_after_days`` +- sbws now takes as an argument the path to a config file (which + contains ``sbws_home``) instead of ``sbws_home`` (which contains the + path to a config file) + +Added +~~~~~ + +- Log line on start up with sbws version, platform info, and library + versions (trac#26751) +- Manual pages (#26926) + +Fixed +~~~~~ + +- Stop deleting the latest.v3bw symlink. Instead, do an atomic rename. + (#26740) +- State file for storing the last time ``sbws scanner`` was started, + and able to be used for storing many other types of state in the + future. (GH#166) +- Log files weren't rotating. Now they are. (#26881) + +Changed +~~~~~~~ + +- Remove test data v3bw file and generate it from the same test. + (#26736) +- Stop using food terms for cleanup-related config options +- cleanup command now cleans up old v3bw files too (#26701) +- Make sbws more compatible with system packages: (#26862) +- Allow a configuration file argument +- Remove directory argument +- Create minimal user configuration when running +- Do not require to run a command to initialize +- Initialize directories when running +- Do not require configuration file inside directories specified by the + configuration + +v0.6.0 (2018-07-11) +------------------- + +**Important changes**: + +- The way users configure logging has changed. No longer are most users + expected to be familiar with how to configure python's standard + logging library with a config file. Instead we've abstracted out the + setting of log level, format, and destinations to make these settings + more accessible to users. Expert users familiar with `the logging + config file + format `__ + can still make tweaks. + +Summary of changes: + +- Make logging configuration easier for the user. +- Add UML diagrams to documentation. They can be found in + docs/source/images/ and regenerated with ``make umlsvg`` in docs/. + +Added +~~~~~ + +- UML diagrams to documentation. In docs/ run ``make umlsvg`` to + rebuild them. Requires graphviz to be installed.(GHPR#226) +- Add metadata to setup.py, useful for source/binary distributions. +- Add possibility to log to system log. (#26683) +- Add option to cleanup v3bw files. (#26701) + +Fixed +~~~~~ + +- Measure relays that have both Exit and BadExit as non-exits, which is + how clients would use them. (GH#217) +- Could not init sbws because of a catch-22 related to logging + configuration. Overhaul how logging is configured. (GH#186 GHPR#224) +- Call write method of V3BWFile class from the object instance. + (#26671) +- Stop calculating median on empty list .(#26666) + +Changed +~~~~~~~ + +- Remove is\_controller\_ok. Instead catch possible controller + exceptions and log them + +Removed +~~~~~~~ + +- Two parsing/plotting scripts in scripts/tools/ that can now be found + at https://github.com/pastly/v3bw-tools + +v0.5.0 (2018-06-26) +------------------- + +**Important changes**: + +- Result format changed, causing a version bump to 4. Updating sbws to + 0.5.0 will cause it to ignore results with version less than 4. + +Summary of changes: + +- Keep previously-generated v3bw files +- Allow a relay to limit its weight based on + RelayBandwidthRate/MaxAdvertisedBandwidth +- 1 CPU usage optimization +- 1 memory usage optimization + +Added +~~~~~ + +- Use a relay's {,Relay}BandwidthRate/MaxAdvertisedBandwidth as an + upper bound on the measurements we make for it. (GH#155) +- Ability to only consider results for a given relay valid if they came + from when that relay is using its most recent known IP address. + Thanks Juga. (GH#154 GHPR#199) +- Maintenance script to help us find functions that are (probably) no + longer being called. +- Integration test(s) for RelayPrioritizer (GHPR#206) +- Git/GitHub usage guidelines to CONTRIBUTING document (GH#208 + GHPR#215) + +Fixed +~~~~~ + +- Make relay priority calculations take only ~5% of the time they used + to (3s vs 60s) by using sets instead of lists when selecting + non-Authority relays. (GH#204) +- Make relay list refreshing take much less time by not allowing worker + threads to dogpile on the CPU. Before they would all start requesting + descriptors from Tor at roughly the same time, causing us to overload + our CPU core and make the process take unnecessarily long. Now we let + one thread do the work so it can peg the CPU on its own and get the + refresh done ASAP. (GH#205) +- Catch a JSON decode exception on malformed results so sbws can + continue gracefully (GH#210 GHPR#212) + +Changed +~~~~~~~ + +- Change the path where the Bandwidth List files are generated: now + they are stored in ``v3bw`` directory, named ``YYmmdd_HHMMSS.v3bw``, + and previously generated ones are kept. A ``latest.v3bw`` symlink is + updated. (GH#179 GHPR#190) +- Code refactoring in the v3bw classes and generation area +- Replace v3bw-into-xy bash script with python script to handle a more + complex v3bw file format (GH#182) + +v0.4.1 (2018-06-14) +------------------- + +Changed +~~~~~~~ + +- If the relay to measure is an exit, put it in the exit position and + choose a non-exit to help. Previously the relay to measure would + always be the first hop. (GH#181) +- Try harder to find a relay to help measure the target relay with two + changes. Essentially: (1) Instead of only picking from relays that + are 1.25 - 2.00 times faster than it by consensus weight, try (in + order) to find a relay that is at least 2.00, 1.75, 1.50, 1.25, or + v1.00 times as fast. If that fails, instead of giving up, (2) pick the + fastest relay in the network instead of giving up. This compliments + the previous change about measuring target exits in the exit + position. + +Fixed +~~~~~ + +- Exception that causes sbws to fall back to one measurement thread. We + first tried fixing something in this area with ``88fae60bc`` but + neglected to remember that ``.join()`` wants only string arguments + and can't handle a ``None``. So fix that. +- Exception when failing to get a relay's ``ed25519_master_key`` from + Tor and trying to do ``.rstrip()`` on a None. +- ``earliest_bandwidth`` being the newest bw not the oldest (thanks + juga0) +- ``node_id`` was missing the character "$" at the beginning diff -Nru sbws-1.0.2/CONTRIBUTING.rst sbws-1.1.0/CONTRIBUTING.rst --- sbws-1.0.2/CONTRIBUTING.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/CONTRIBUTING.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,292 +0,0 @@ -.. _contributing: - -Contributing to Simple Bandwidth Scanner -========================================= - -Thank you for your interest in Simple Bandwidth Scanner (``sbws``). - -Examples of contributions include: - -* Bug reports, feature requests -* Code/documentation patches - -Bug reports or feature requests ---------------------------------- - -* Check that it has not been already reported. - -.. _ticket-ref: - -* Open a ticket in - `Tor Project Trac `_ - and assign the component to ``Core Tor``/``sbws``. - -Code/documentation patches ---------------------------- - -The sbws canonical repository is https://gitweb.torproject.org/sbws.git, -but we review patches using the Github canonical repository -(https://github.com/torproject/sbws) Pull Requests (PR). - -To know more about ``sbws`` code, - -.. seealso:: - - - :ref:`dev_doc` - - ``./docs/source/testing.rst`` (or `testing `_ - or :ref:`testing`). - - ``./docs/source/documenting.rst`` (or `documenting `_ - or :ref:`documenting`). - -The following are guidelines we aim to follow. - -Steps to create a PR -~~~~~~~~~~~~~~~~~~~~~ - -1. Create a ticket in Tor Project Trac (:ref:`Open ticket `) -2. Clone ``sbws`` via the Github web interface - https://github.com/torproject/sbws -3. Clone the repository locally -4. Install ``sbws`` as explained in ./INSTALL.rst and ./TESTING.rst - Use ``pip install -e <>`` -5. If needed install the documentation and build it as explained in - ./DOCUMENTATION.rst -6. Create a new branch, named ``ticketXXX``. - Optionally, name it with a string explaining what it does, - ie ``ticketXXX_contributing`` -7. Write code (:ref:`codestyle-ref`), tests, documentation, - extra files (:ref:`extrafiles-ref`), commit (:ref:`commits-ref`), etc. -8. Ensure tests pass (./TESTING.rst). -9. Push your branch to your repository. If you have an account in Travis, - you can see whether it pass the tests in Github and in - https://travis-ci.org/youruser/sbws/ -10. Create a PR from your branch to https://github.com/torproject/sbws -11. Change the Trac ticket status to ``needs_review`` - -.. _codestyle-ref: - -Code style -~~~~~~~~~~ - -Follow the Zen of Python (:pep:`20`) - -.. code-block:: pycon - - >>> import this - The Zen of Python, by Tim Peters - - Beautiful is better than ugly. - Explicit is better than implicit. - Simple is better than complex. - Complex is better than complicated. - Flat is better than nested. - Sparse is better than dense. - Readability counts. - -Code should adhere to the :pep:`8` guidelines. -Before release 1.0.0, some guidelines have not been followed, -such as the ordering the inputs (:pep:`8#imports`). - -External link: `Code Style `_ - -All functions, methods and classes should have :pep:`0257` -(except ``__repr__`` and ``__str__``). -Before release 1.0.0, some docstrigs do not have 3 double quotes ``"""`` -(:pep:`0257#id15`). - -External link: `Documentation `_ - -New features should add a corresponding documentation in /docs. - -An editor compatible with `EditorConfig `_ will -help you to follow the general formatting code style. - -Timestamps must be in UTC. It is prefered to use ``datetime`` objects or -Unix timestamps. Timestamps read by the user should be always formatted in -`ISO 8601 `_ - -Functional style is prefered: - -- use list comprenhensions lambda, map, reduce -- avoid reasigigning variables, instead create new ones -- use ``deepcopy`` when passing list of objects to a function/method -- classes should change attributes only in one method (other than __init__?) - -[FUNC]_ - -In general, do not reinvent the wheel, use Python native modules as ``logging``, -instead of implementing similar functionality. -Or use other packages when the new dependency can be extra, for instance -`vulture`_. - -.. _`extrafiles-ref`: - -Extra required files -~~~~~~~~~~~~~~~~~~~~~ - -Any non-trivial change should contain tests. See ./TESTING.rst. -When running tests, currently ``flake8`` informs on some PEP8 errors/warnings, -but not all. - -Document your changes in ./CHANGELOG.rst following `keep a changelog`_. -Reference the Tor Project Trac ticket (example: ``#12345``) or -Github ticket (example: ``GH#123``). - -.. _commits-ref: - -Commits -~~~~~~~~~ - -Try to make each commit a logically separate changes.:: - - As a general rule, your messages should start with a single line that’s - o more than about 50 characters and that describes the changeset concisely, - followed by a blank line, followed by a more detailed explanation. - The Git project requires that the more detailed explanation include - your motivation for the change and contrast its implementation with - previous behavior — this is a good guideline to follow. - It’s also a good idea to use the imperative present tense in these messages. - In other words, use commands. - Instead of "I added tests for" or "Adding tests for," use "Add tests for." - -[DIST]_ - -Template originally written by `Tim Pope`_: :ref:`example commit ` - -Code being reviewed workflow -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When a PR is being reviewed, new changes might be needed: - -- If the change does not modify a previous change, create new commits and push. -- If the change modifies a previous change and it's small, - `git commit fixup `_ - should be used. When it is agreed that the PR is ready, create a new branch - named ``mybranch_02`` and run: - - .. code-block:: bash - - rebase --autosquash - - push, create new PR and close old PR mentioning the number of the new PR. -- If the review takes long and when it's ready code related to the PR has changed - in master, create a new branch named ``mybranch_02`` and run: - - .. code-block:: bash - - rebase master - - push, create new PR and close old PR mentioning the number of the new PR. - -[MERG]_ - -.. _review-ref: - -Reviewing code ----------------- - -All code should be peer-reviewed. Two reasons for this are:: - - Because a developer cannot think of everything at once; - Because a fresh pair of eyes may spot an error, a corner-case in the code, - insufficient documentation, a missing consistency check, etc. - -[REVI]_ - -Reviewers: - -- Should let the contributor know what to improve/change. -- Should not push code to the contributor's branch. -- Should wait for contributor's changes or feedback after changes are requested, - before merging or closing a PR. -- Should merge (not rebase) the PR. -- If rebase is needed due to changes in master, the contributor should create - a new branch named `xxx_rebased` based on the reviewed branch, rebase and - create a new PR from it, as explained above. -- If new changes are needed when the contributor's branch is ready to merge, - the reviewer can create a new branch based on the contributor's branch, - push the changes and merge that PR. - The contributor should be notified about it. -- If the reviewer realize that new changes are needed after the PR has been - merged, the reviewer can push to master, notifying the contributor about the - changes. -- Because currently there are not many reviewers, reviewers can merge their own - PR if there was not any feedback after a week. -- Should not push directly to master, unless changes are trivial (typos, - extra spaces, etc.) -- Should not push to master new features while there are open PRs to review. - -Currently, the reviewers are the persons that have contributed to the code: -pastly, teor, juga. - -.. _releases-ref: - -Releases ----------- - -Releases follow `semantic versioning`_. -Until release 1.0.0 is reached, this project is not considered production -ready. - -Currently development happens in master, this might change from release 1.0.0 - -so that master has the last release changes, and development happens in the -next release branch. - -Before major releases, ensure that: - -- Installation from scratch, as specified in ./INSTALL.md, must success. -- All tests must pass. -- Tor must be able to parse the produced bw files - (current way is manual) - - .. todo:: - - Test that run Tor as dirauth and parse the files - -- Bandwidth files must produce graphs compatible with Torflow - (current way to test it is manual) - - .. todo:: - - Implement something to compare error with current consensus. -- A dirauth should be able to understand the documentation, otherwise the - documentation should be clarified. - - -.. _commit-msg: - -Example commit message ------------------------ - -:: - - Short (50 chars or less) summary of changes - - More detailed explanatory text, if necessary. Wrap it to - about 72 characters or so. In some contexts, the first - line is treated as the subject of an email and the rest of - the text as the body. The blank line separating the - summary from the body is critical (unless you omit the body - entirely); tools like rebase can get confused if you run - the two together. - - Further paragraphs come after blank lines. - - - Bullet points are okay, too - - - Typically a hyphen or asterisk is used for the bullet, - preceded by a single space, with blank lines in - between, but conventions vary here - - -.. rubric:: External eferences - -.. [DIST] https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project -.. [MERG] https://www.atlassian.com/git/tutorials/merging-vs-rebasing -.. [REVI] https://doc.sagemath.org/html/en/developer/reviewer_checklist.html -.. [FUNC] https://medium.com/@rohanrony/functional-programming-in-python-1-lambda-map-filter-reduce-zip-8739ea144186 -.. _tim pope: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html -.. _`keep a changelog`: https://keepachangelog.com/en/1.0.0/ -.. _`semantic versioning`: https://semver.org/ -.. _`vulture`: https://pypi.org/project/vulture/ diff -Nru sbws-1.0.2/debian/changelog sbws-1.1.0/debian/changelog --- sbws-1.0.2/debian/changelog 2018-11-01 19:04:00.000000000 +0000 +++ sbws-1.1.0/debian/changelog 2019-04-05 12:43:15.000000000 +0000 @@ -1,3 +1,28 @@ +sbws (1.1.0-1) unstable; urgency=medium + + * New upstream version + * d/sbws.docs: Moved file upstream + * d/patches: Update to upstream + * d/sbws.dirs: Rename .dir to dirs + * d/sbws-doc.doc-base: Add file + * Bump debhelper to 12 + * d/control: bump standards-version to 4.3.0 + (no changes needed) + * d/control: Add Built-Using to sbws-doc + * d/copyright: Update to 2019 + * d/copyright: Exclude *.egg-info file. + They are not present in the source distribution but created when + installing the package. + * d/sbws.docs: Install the examples directory + not only the file included, since the link in INSTALL.rst link to + /examples/X. + * d/sbws.service: Restrict read/write directories + * d/sbws.postrm: Remove HOME if empty + and mask/umask/purg deb-systemd-helper. + To do not get error with piuparts on purge. + + -- ju xor Fri, 05 Apr 2019 12:43:15 +0000 + sbws (1.0.2-1) unstable; urgency=medium * New upstream version diff -Nru sbws-1.0.2/debian/compat sbws-1.1.0/debian/compat --- sbws-1.0.2/debian/compat 2018-11-01 19:04:00.000000000 +0000 +++ sbws-1.1.0/debian/compat 2019-04-05 12:43:15.000000000 +0000 @@ -1 +1 @@ -11 +12 diff -Nru sbws-1.0.2/debian/control sbws-1.1.0/debian/control --- sbws-1.0.2/debian/control 2018-11-01 19:04:00.000000000 +0000 +++ sbws-1.1.0/debian/control 2019-04-05 12:43:15.000000000 +0000 @@ -5,7 +5,7 @@ Priority: optional Build-Depends: adduser, - debhelper (>= 11~), + debhelper (>= 12~), dh-python, pkg-config, dvipng, @@ -18,7 +18,7 @@ python3-sphinx, python3-recommonmark, texlive-latex-extra, -Standards-Version: 4.2.1 +Standards-Version: 4.3.0 Vcs-Browser: https://salsa.debian.org/pkg-privacy-team/sbws Vcs-Git: https://salsa.debian.org/pkg-privacy-team/sbws.git Homepage: https://sbws.readthedocs.io/ @@ -58,6 +58,7 @@ Architecture: all Depends: ${misc:Depends}, ${sphinxdoc:Depends} +Built-Using: ${sphinxdoc:Built-Using} Description: Simple Bandwidth Scanner Tor bandwidth scanner that generates bandwidth list (measurements) files to be read by Directory Authorities. diff -Nru sbws-1.0.2/debian/copyright sbws-1.1.0/debian/copyright --- sbws-1.0.2/debian/copyright 2018-11-01 19:04:00.000000000 +0000 +++ sbws-1.1.0/debian/copyright 2019-04-05 12:43:15.000000000 +0000 @@ -2,14 +2,13 @@ Upstream-Name: sbws Upstream-Contact: Matt Traudt , juga Source: https://gitweb.torproject.org/sbws.git -Files-Excluded: *.egg-info Files: * Copyright: 2018 Matt Traudt License: CC0-1.0 Files: debian/* -Copyright: 2018 ju xor +Copyright: 2018, 2019 ju xor License: CC0-1.0 License: CC0-1.0 diff -Nru sbws-1.0.2/debian/patches/0001-Update-remove-third-party-requests.patch sbws-1.1.0/debian/patches/0001-Update-remove-third-party-requests.patch --- sbws-1.0.2/debian/patches/0001-Update-remove-third-party-requests.patch 2018-11-01 19:04:00.000000000 +0000 +++ sbws-1.1.0/debian/patches/0001-Update-remove-third-party-requests.patch 2019-04-05 12:43:15.000000000 +0000 @@ -6,8 +6,6 @@ README.md | 2 -- 1 file changed, 2 deletions(-) -diff --git a/README.md b/README.md -index c98efc8..d17c7c4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ @@ -16,5 +14,5 @@ -[![Build Status](https://travis-ci.org/torproject/sbws.svg?branch=master)](https://travis-ci.org/https://travis-ci.org/torproject/sbws) - Simple Bandwidth Scanner (called `sbws`) is a Tor bandwidth scanner that - produces bandwidth measurements files to be used by Directory Authorities. + generates bandwidth files to be used by Directory Authorities. diff -Nru sbws-1.0.2/debian/sbws.dir sbws-1.1.0/debian/sbws.dir --- sbws-1.0.2/debian/sbws.dir 2018-11-01 19:04:00.000000000 +0000 +++ sbws-1.1.0/debian/sbws.dir 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -etc/sbws diff -Nru sbws-1.0.2/debian/sbws.dirs sbws-1.1.0/debian/sbws.dirs --- sbws-1.0.2/debian/sbws.dirs 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/debian/sbws.dirs 2019-04-05 12:43:15.000000000 +0000 @@ -0,0 +1 @@ +etc/sbws diff -Nru sbws-1.0.2/debian/sbws-doc.doc-base sbws-1.1.0/debian/sbws-doc.doc-base --- sbws-1.0.2/debian/sbws-doc.doc-base 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/debian/sbws-doc.doc-base 2019-04-05 12:43:15.000000000 +0000 @@ -0,0 +1,9 @@ +Document: sbws +Title: sbws documentation +Author: juga +Abstract: This manual describes what sbws is, and how it can be used. +Section: Network/Monitoring + +Format: HTML +Index: /usr/share/doc/sbws-doc/html/index.html +Files: /usr/share/doc/sbws-doc/html/*.html diff -Nru sbws-1.0.2/debian/sbws.docs sbws-1.1.0/debian/sbws.docs --- sbws-1.0.2/debian/sbws.docs 2018-11-01 19:04:00.000000000 +0000 +++ sbws-1.1.0/debian/sbws.docs 2019-04-05 12:43:15.000000000 +0000 @@ -1,5 +1,4 @@ AUTHORS.md -CONTRIBUTING.rst DEPLOY.rst README.md -docs/source/examples/config.example.ini +docs/source/examples diff -Nru sbws-1.0.2/debian/sbws.postrm sbws-1.1.0/debian/sbws.postrm --- sbws-1.0.2/debian/sbws.postrm 2018-11-01 19:04:00.000000000 +0000 +++ sbws-1.1.0/debian/sbws.postrm 2019-04-05 12:43:15.000000000 +0000 @@ -2,13 +2,36 @@ set -e case $1 in + remove) + # From /usr/share/debhelper/autoscripts/postrm-systemd + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper mask sbws.service >/dev/null + fi + exit 0 + ;; purge) - if which deluser >/dev/null 2>&1 ; then - deluser --quiet --system sbws > /dev/null || true - else - echo >&2 "Not removing sbws system account because deluser command was not found" - fi - ;; + if which deluser >/dev/null 2>&1 ; then + # --remove-home does not actually remove the home and needs mount + deluser --quiet --system sbws > /dev/null || true + else + echo >&2 "Not removing sbws system account because deluser command was not found" + fi + if [ -d /var/lib/sbws ]; then + # Remove home directory if it is empty + if [ $(find /var/lib/sbws | wc -l) -eq 1 ]; then + echo "Home directory is empty, removing it." + rmdir /var/lib/sbws + else + echo "Home directory is not empty, not removing it." + fi + fi + # From /usr/share/debhelper/autoscripts/postrm-systemd + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper purge sbws.service >/dev/null + deb-systemd-helper unmask sbws.service >/dev/null + fi + exit 0 + ;; esac #DEBHELPER# diff -Nru sbws-1.0.2/debian/sbws.service sbws-1.1.0/debian/sbws.service --- sbws-1.0.2/debian/sbws.service 2018-11-01 19:04:00.000000000 +0000 +++ sbws-1.1.0/debian/sbws.service 2019-04-05 12:43:15.000000000 +0000 @@ -9,6 +9,15 @@ ExecStart=/usr/bin/sbws scanner User=sbws +# Hardening, to complete in the future +NoNewPrivileges=yes +PrivateTmp=yes +PrivateDevices=yes +ProtectHome=yes +ProtectSystem=full +ReadOnlyDirectories=/ +ReadWriteDirectories=-/var/lib/sbws +ReadWriteDirectories=-/run [Install] WantedBy=multi-user.target diff -Nru sbws-1.0.2/DEPLOY.rst sbws-1.1.0/DEPLOY.rst --- sbws-1.0.2/DEPLOY.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/DEPLOY.rst 2019-03-29 13:47:26.000000000 +0000 @@ -19,18 +19,42 @@ - A Web server installed and running that supports HTTP GET, HEAD and Range (:rfc:`7233`) requests. ``Apache`` HTTP Server and ``Nginx`` support them. -- Optional support for TLS +- TLS support to avoid HTTP content caches at the various exit nodes. +- Certificates can be self-signed. - A large file; at the time of writing, at least 1 GiB in size + It can be created running:: + + head -c $((1024*1024*1024)) /dev/urandom > 1GiB + +- A fixed IP address or a domain name. +- Bandwidth: at least 12.5MB/s (100 Mbit/s). +- Network traffic: around 12-15GB/day. scanner setup ---------------------- -Install sbws according to ``./INSTALL.rst`` (or `/INSTALL.rst `_ -or :ref:`install`). +Install sbws according to ``_ (in the local directory or GitHub) +or ``_ (local build or Read the Docs). + +To run the ``scanner`` it is mandatory to create a configuration file with at +least one ``destination``. +It is recommended to set several ``destination``s so that the ``scanner`` can +continue if one fails. + +If ``sbws`` is installed from the Debian package, then create a file in +``/etc/sbws/sbws.ini`` like in the following example: + +.. literalinclude:: /examples/sbws.example.ini + :caption: Example sbws.example.ini + +If ``sbws`` is installed from the sources as a non-root user then create the +file in ``~/.sbws.ini``. -To configure destinations, create a configuration file according to -``./docs/source/man_sbws.ini.rst`` (or `/docs/source/man_sbws.ini.rst `_ -or :doc:`man_sbws.ini` or ``man sbws.ini``) +More details about the configuration file can be found in +``./docs/source/man_sbws.ini.rst`` (in the local directory or GitHub) or +``_ (local build or Read the Docs) or +``man sbws.ini`` (system package). -See also ``./docs/source/man_sbws.rst`` (or `/docs/source/man_sbws.rst `_ or -:doc:`man_sbws` or ``man sbws``) manual page. +See also ``./docs/source/man_sbws.rst`` (in the local directory or GitHub) or +``_ (local build or Read the Docs) or ``man sbws`` (system +package). diff -Nru sbws-1.0.2/docs/source/activity_all.puml sbws-1.1.0/docs/source/activity_all.puml --- sbws-1.0.2/docs/source/activity_all.puml 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/activity_all.puml 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,25 @@ +@startuml + +start + +while (no SIGINT/SIGTERM?) + + while (next relay to measure?) + + :Select a destination; + + :Select a second relay; + + :Build a circuit; + + :HTTP GET (Range-Bytes); + + :Store measurement; + + endwhile + +endwhile + +stop + +@enduml \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/activity_second_relay.puml sbws-1.1.0/docs/source/activity_second_relay.puml --- sbws-1.0.2/docs/source/activity_second_relay.puml 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/activity_second_relay.puml 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,30 @@ +@startuml + +start + +if (relay to measure is exit?) then (yes) + :obtain non-exits; +else (no) + :obtain an exits + without bad flag + that can exit + to port 443; +endif +:potential second relays; +:obtain a relay +from potential +sencond relays +randomly; +if (second relay has 2x bandwidth?) then (yes) +elseif (other second relay has 1.5x bandwidth?) then (yes) +elseif (other second relay has 1x bandwidth?) then (yes) +else (nothing) + stop +endif +:second relay selected!; +:Build a circuit +whith exit as +second hop; +stop + +@enduml \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/bandwidth_authorities.rst sbws-1.1.0/docs/source/bandwidth_authorities.rst --- sbws-1.0.2/docs/source/bandwidth_authorities.rst 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/bandwidth_authorities.rst 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,28 @@ +Bandwidth authorities in metrics +================================= + +Current bandwidth authorities +----------------------------- + +.. image:: images/bwauth.* + :alt: bandwidth authorities in metrics + +https://metrics.torproject.org/rs.html + +(flag:Authority) + +Bandwidth Authorities - Measured Relays past 7 days +--------------------------------------------------- + +.. image:: images/bwauth_measured_7days.png + :alt: bandwidth measured in the past 7 days + +https://consensus-health.torproject.org/graphs.html + +Bandwidth Authorities - Measured Relays past 90 days +---------------------------------------------------- + +.. image:: images/bwauth_measured_90days.png + :alt: bandwidth measured in the past 90 days + +https://consensus-health.torproject.org/graphs.html diff -Nru sbws-1.0.2/docs/source/bandwidth_distribution.rst sbws-1.1.0/docs/source/bandwidth_distribution.rst --- sbws-1.0.2/docs/source/bandwidth_distribution.rst 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/bandwidth_distribution.rst 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,38 @@ +Relays' bandwidth distribution +=================================== + +sbws raw measurements compared to Torflow measurements +------------------------------------------------------ + +.. image:: images/43710932-ac1eeea8-9960-11e8-9e7e-21fddff2f7a3.png + :alt: sbws and torflow raw measurements distribution + + +.. image:: images/43710933-ac95e0bc-9960-11e8-9aaf-0bb1f83b65e2.png + :alt: sbws and torflow raw measurements distribution 2 + + +sbws linear scaling +-------------------- + +Multiply each relay bandwidth by ``7500/median`` + +See bandwidth_file_spec_ appendix B to know how about linear scaling. + +Code: :func:`sbws.lib.v3bwfile.sbws_scale` + +.. image:: images/20180901_163442.png + :alt: sbws linear scaling + + +sbws Torflow scaling +----------------------- + +See bandwidth_file_spec_ appendix B to know how about torflow scaling. + +Code: :func:`sbws.lib.v3bwfile.torflow_scale` + +.. image:: images/20180901_164014.png + :alt: sbws torflow scaling + +.. _bandwidth_file_spec: https://gitweb.torproject.org/torspec.git/tree/bandwidth-file-spec.txt diff -Nru sbws-1.0.2/docs/source/CHANGELOG.md sbws-1.1.0/docs/source/CHANGELOG.md --- sbws-1.0.2/docs/source/CHANGELOG.md 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/CHANGELOG.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,288 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [1.0.2] - 2018-11-10 - -### Fixed - -- Update bandwidth file specification version in the `generator` (#28366). -- Use 5 "=" characters as terminator in the bandwidth files (#28379) - -### Changed - -- Include the headers about eligible relays in all the bandwidth files, - not only in the ones that does not have enough eligible relays (#28365). - -## [1.0.1] - 2018-11-01 - -### Changed - -- Change default directories when sbws is run from a system service (#28268). - -## [1.0.0] - 2018-10-29 - -**Important changes**: - -- `generate` includes extra statistics header lines when the number of - eligible relays to include is less than the 60% of the network. - It does not include the relays' lines. -- Speed up `scanner` by disabling RTT measurements and waiting for - measurement threads before prioritizing again the list of relays to measure. - -### Fixed - -- Update python minimal version in setup (#28043) -- Catch unhandled exception when we fail to resolve a domain name (#28141) -- Bandwidth filtered is the maximum between the bandwidth measurements and - their mean, not the minimum (#28215) -- Stop measuring the same relay by two threads(#28061) - -### Changed - -- Move ``examples/`` to ``docs/`` (#28040) -- Number of results comparison and number of results away from each other are - incorrect (#28041) -- Stop removing results that are not away from some other X secs (#28103) -- Use secs-away when provided instead of data_period (#28105) -- Disable measuring RTTs (#28159) -- Rename bandwidth file keyvalues (#28197) - -## Added - -- Write bw file only when the percentage of measured relays is bigger than 60% - (#28062) -- When the percentage of measured relays is less than the 60%, do not include - the relays in the bandwidth file and instead include some statistics in the - header (#28076) -- When the percentage of measured relays is less than the 60% and it was more - before, warn about it (#28155) -- When the difference between the total consensus bandwidth and the total - in the bandwidth lines is larger than 50%, warn (#28216) -- Add documentation about how the bandwidth measurements are selected and - scaled before writing them to the Bandwidth File (#27692) - -## [0.8.0] - 2018-10-08 - -**Important changes**: - -- Implement Torflow scaling/aggregation to be able to substitute Torflow with - sbws without affecting the bandwidth files results. -- Change stem dependency to 1.7.0, which removes the need for `dependency_links`` -- Update and cleanup documentation - -### Added - -- Add system physical requirements section to INSTALL (#26937) -- Warn when there is not enough disk space (#26937) -- Implement Torflow scaling (#27108) -- Create methods to easy graph generation and obtain statistics - to compare with current torflow results.(#27688) -- Implement rounding bw in bandwidth files to 2 insignificant digits(#27337) -- Filter results in order to include relays in the bandwidth file that:(#27338) - - have at least two measured bandwidths - - the measured bandwidths are within 24 hours of each other - - have at least two descriptor observed bandwidths - - the descriptor observed bandwidths are within 24 hours of each other - -### Fixed - -- Broken environment variable in default sbws config. To use envvar $FOO, write - $$FOO in the config. -- Stop using directory as argument in integration tests (#27342) -- Fix typo getting configuration option to allow logging to file (#27960) -- Set int type to new arguments that otherwise would be string (#27918) -- Stop printing arguments default values, since they are printed by default - (#27916) -- Use dash instead of underscore in new cli argument names (#27917) - -### Changed - -- sbws install doc is confusing (#27341) - - Include system and Python dependencies in `INSTALL`. - - Include dependencies for docs and tests in `INSTALL`. - - Point to `DEPLOY` to run sbws. - - Remove obsolete sections in `INSTALL` - - Simplify `DEPLOY`, reuse terms in the `glossary`. - - Remove obsolete ``sbws init`` from `DEPLOY`. - - Point to config documentation. - - Add, unify and reuse terms in `glossary`. -- refactor v3bwfile (#27386): move scaling method inside class -- use custom ``install_command`` to test installation commands while - ``dependency_links`` is needed until #26914 is fixed. (#27704) -- documentation cleanup (#27773) - - split, merge, simplify, extend, reorganize sections and files -- generate scales as Torflow by default (#27976) -- Replace stem ``dependency_links`` by stem 1.7.0 (#27705). This also eliminates - the need for custom ``install_command`` in tox. - -## [0.7.0] - 2018-08-09 - -**Important changes**: - -- `cleanup/stale_days` is renamed to `cleanup/data_files_compress_after_days` -- `cleanup/rotten_days` is renamed to `cleanup/data_files_delete_after_days` -- sbws now takes as an argument the path to a config file (which contains - `sbws_home`) instead of `sbws_home` (which contains the path to a config -file) - -### Added - -- Log line on start up with sbws version, platform info, and library versions -(trac#26751) -- Manual pages (#26926) - -### Fixed - -- Stop deleting the latest.v3bw symlink. Instead, do an atomic rename. - (#26740) -- State file for storing the last time `sbws scanner` was started, and able to - be used for storing many other types of state in the future. (GH#166) -- Log files weren't rotating. Now they are. (#26881) - -### Changed - -- Remove test data v3bw file and generate it from the same test. (#26736) -- Stop using food terms for cleanup-related config options -- Cleanup command now cleans up old v3bw files too (#26701) -- Make sbws more compatible with system packages: (#26862) - - Allow a configuration file argument - - Remove directory argument - - Create minimal user configuration when running - - Do not require to run a command to initialize - - Initialize directories when running - - Do not require configuration file inside directories specified by the - configuration - -## [0.6.0] - 2018-07-11 - -**Important changes**: - -- The way users configure logging has changed. No longer are most users - expected to be familiar with how to configure python's standard logging -library with a config file. Instead we've abstracted out the setting of log -level, format, and destinations to make these settings more accessible to -users. Expert users familiar with [the logging config file format][logconffmt] -can still make tweaks. - -Summary of changes: - -- Make logging configuration easier for the user. -- Add UML diagrams to documentation. They can be found in docs/source/images/ - and regenerated with `make umlsvg` in docs/. - -[logconffmt]: https://docs.python.org/3/library/logging.config.html#logging-config-fileformat - -### Added - -- UML diagrams to documentation. In docs/ run `make umlsvg` to rebuild them. - Requires graphviz to be installed.(GHPR#226) -- Add metadata to setup.py, useful for source/binary distributions. -- Add possibility to log to system log. (#26683) -- Add option to cleanup v3bw files. (#26701) - -### Fixed - -- Measure relays that have both Exit and BadExit as non-exits, which is how - clients would use them. (GH#217) -- Could not init sbws because of a catch-22 related to logging configuration. - Overhaul how logging is configured. (GH#186 GHPR#224) -- Call write method of V3BWFile class from the object instance. (#26671) -- Stop calculating median on empty list .(#26666) - -### Changed - -- Remove is_controller_ok. Instead catch possible controller exceptions and -log them - -### Removed - -- Two parsing/plotting scripts in scripts/tools/ that can now be found at - - -## [0.5.0] - 2018-06-26 - -**Important changes**: - -- Result format changed, causing a version bump to 4. Updating sbws to 0.5.0 - will cause it to ignore results with version less than 4. - -Summary of changes: - -- Keep previously-generated v3bw files -- Allow a relay to limit its weight based on - RelayBandwidthRate/MaxAdvertisedBandwidth -- 1 CPU usage optimization -- 1 memory usage optimization - -### Added - -- Use a relay's {,Relay}BandwidthRate/MaxAdvertisedBandwidth as an upper bound - on the measurements we make for it. (GH#155) -- Ability to only consider results for a given relay valid if they came from - when that relay is using its most recent known IP address. Thanks Juga. -(GH#154 GHPR#199) -- Maintenance script to help us find functions that are (probably) no longer - being called. -- Integration test(s) for RelayPrioritizer (GHPR#206) -- Git/GitHub usage guidelines to CONTRIBUTING document (GH#208 GHPR#215) - -### Fixed - -- Make relay priority calculations take only ~5% of the time they used to (3s - vs 60s) by using sets instead of lists when selecting non-Authority relays. -(GH#204) -- Make relay list refreshing take much less time by not allowing worker threads - to dogpile on the CPU. Before they would all start requesting descriptors -from Tor at roughly the same time, causing us to overload our CPU core and make -the process take unnecessarily long. Now we let one thread do the work so it -can peg the CPU on its own and get the refresh done ASAP. -(GH#205) -- Catch a JSON decode exception on malformed results so sbws can continue - gracefully (GH#210 GHPR#212) - -### Changed - -- Change the path where the Bandwidth List files are generated: now they are - stored in `v3bw` directory, named `YYmmdd_HHMMSS.v3bw`, and previously -generated ones are kept. A `latest.v3bw` symlink is updated. (GH#179 GHPR#190) -- Code refactoring in the v3bw classes and generation area -- Replace v3bw-into-xy bash script with python script to handle a more complex - v3bw file format (GH#182) - -## [0.4.1] - 2018-06-14 - -### Changed - -- If the relay to measure is an exit, put it in the exit position and choose a - non-exit to help. Previously the relay to measure would always be the first -hop. (GH#181) -- Try harder to find a relay to help measure the target relay with two changes. - Essentially: (1) Instead of only picking from relays that are 1.25 - 2.00 -times faster than it by consensus weight, try (in order) to find a relay that -is at least 2.00, 1.75, 1.50, 1.25, or 1.00 times as fast. If that fails, -instead of giving up, (2) pick the fastest relay in the network instead of -giving up. This compliments the previous change about measuring target exits in -the exit position. - -### Fixed - -- Exception that causes sbws to fall back to one measurement thread. We first - tried fixing something in this area with `88fae60bc` but neglected to -remember that `.join()` wants only string arguments and can't handle a `None`. -So fix that. -- Exception when failing to get a relay's `ed25519_master_key` from Tor and - trying to do `.rstrip()` on a None. -- `earliest_bandwidth` being the newest bw not the oldest (thanks juga0) -- `node_id` was missing the character "$" at the beginning - -[Unreleased]: https://github.com/pastly/simple-bw-scanner/compare/v0.7.0...master -[0.7.0]: https://github.com/pastly/simple-bw-scanner/compare/v0.6.0...v0.7.0 -[0.6.0]: https://github.com/pastly/simple-bw-scanner/compare/v0.5.0...v0.6.0 -[0.5.0]: https://github.com/pastly/simple-bw-scanner/compare/v0.4.1...v0.5.0 -[0.4.1]: https://github.com/pastly/simple-bw-scanner/compare/v0.4.0...v0.4.1 diff -Nru sbws-1.0.2/docs/source/CHANGELOG.rst sbws-1.1.0/docs/source/CHANGELOG.rst --- sbws-1.0.2/docs/source/CHANGELOG.rst 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/CHANGELOG.rst 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,459 @@ +Changelog +========= + +All notable changes to this project will be documented in this file. + +The format is based on `Keep a +Changelog `__ and this project +adheres to `Semantic Versioning `__. + +v1.1.0 (2019-03-27) +------------------- + +New +~~~ + +- V3bwfile: Report excluded relays. + Closes: #28565. +- V3bwfile: Add time to report half network. + Closes: #28983 +- Destination: Recover destination when it failed. + Closes: #29589. +- V3bwfile: Report relays that fail to be measured. + Closes: #28567. +- V3bwfile: Report relays that are not measured measured. + Closes: #28566 +- V3bwfile: Add KeyValues to monitor relays. + Closes: #29591. +- Docs: document that authorities are not measured. + Closes: #29722 +- Scanner: Warn when there is no progress. + Closes: #28652 + +Fix +~~~ +- v3bwfile: Report relays even when they don't reach a minimum number. + Closes: #29853. +- Minor fixes. Closes #29891. +- Relaylist: Convert consensus bandwidth to bytes. + + +v1.0.5 (2019-03-06) +------------------- + +- Release v1.0.5. + this time with the correct version + +v1.0.4 (2019-03-06) +------------------- + +- Release v1.0.4. + because there was a commit missing between `1.0.3` and `1.0.4-dev0` + and what is released as `1.0.3` has version `1.0.4-dev0` and it + can not be fixed now. + +v1.0.3 (2019-02-28) +------------------- + +Fixed +~~~~~~ + +- scanner: check that ResultDump queue is not full + Fixes bug #28866. Bugfix v0.1.0. +- config: set stdout log level to cli argument. Closes: #29199 +- cleanup: Use getpath to get configuration paths. Bugfix v0.7.0. +- destination: stop running twice usability tests. + Fixes bug #28897. Bugfix v0.3.0 +- globals, stem: explain where torrc options are. + Fixes bug #28646. Bugfix v0.4.0 +- stem: disable pad connections. Fixes bug 28692. Bugfix v0.4.0 +- generate: Load all results, including error ones. + Closes #29568. Bugfix v0.4.0 (line introduced in v0.1.0). +- relayprioritizer: Stop prioritizing relays that tend to fail. + Fixes bug #28868. Bugfix v0.1.0 +- circuitbuilder: Stop building the circuit 3 times. + Fixes bug #29295. Bugfix v0.1.0. +- docs: add verify option to man and example. + Closes bug #28788. Bugfix v0.4.0. +- CI: run scanner using the test network. Fixes bug #28933. Bugfix v0.1.0. +- scanner: catch SIGINT in the main loop. Fixes bug #28869. Bugfix v0.1.0. +- Stop including tests network as binary blob. Fixes bug #28590. Bugfix v0.4.0. +- relaylist: remove assertions that fail measurement. + Closes #28870. Bugfix v0.4.0 +- config: Use configuration provided as argument. + Fixes bug #28724. Bugfix v0.7.0. +- stem: parse torrc options that are only a key. + Fixes bug #28715. Bugfix v0.1.1 +- stem: Stop merging multiple torrc options with the same name. + Fixes bug #28738. Bugfix v0.1.1 +- docs: add note about syslog when running systemd. + Closes bug #28761. Bugfix v0.6.0 +- CI: include deb.torproject.org key. + Closes #28922. Bugfix v1.0.3-dev0 +- config: stop allowing http servers without tls. + Fixes bug #28789. Bugfix v0.2.0. +- Make info level logs more clear and consistent. + Closes bug #28736. Bugfix v0.3.0. +- CI: check broken links in the docs. Closes #28670. +- docs: add scanner and destination requirements. + Closes bug #28647. Bugfix v0.4.0 +- generate: use round_digs variable name in methods. + Closes bug #28602. Bugfix 1.0.3-dev0 +- docs: Change old broken links in the documentation. Closes #28662. +- docs: replace http by https in links. Closes #28661. +- Fix git repository link. Fixes bug #28762. Bugfix v1.0.0. +- docs: add example destination in DEPLOY. Closes #28649. +- docs: Change links to be interpreted by ReST. Closes #28648. +- Force rtfd.io to install the package. Closes bug #28601. +- config: continue when the file is not found. Closes: #28550. +- Stop resolving domains locally and check same flags for the 2nd hop. + Closes bug #28458, #28471. Bugfix 1.0.4. +- Limit the relays' bandwidth to their consensus bandwidth. Closes #28598. +- globals: add torrc logging options. Closes #28645. Bugfix v0.2.0. +- Limit bandwidth to the relay MaxAdvertisedBandwidth + Fixes bug #28588. Bugfix 0.8.0. +- Exclude results, then check for the minimum number. Closes bug 28572. +- Make sbws round to 3 significant figures in torflow rounding mode. + Bugfix on 27337 in sbws 1.0. Part of 28442. + +Changed +~~~~~~~~ + +- tests: remove unused testnets. Fixes bug #29046. Bugfix v0.4.0. +- scanner, destination: Log all possible exceptions. +- docs: Update/improve documentation on how the scanner/generator work. + Closes: #29149 +- Requests: Change make_session to use the TimedSession. +- CI: change to Ubuntu Xenial. +- docs: stop editing changelog on every bug/ticket. Closes ticket #28572. +- Change sbws scaling method to torflow. Closes: #28446. +- Round bandwidths to 2 significant digits by default. + Implements part of proposal 276. Implements 28451. + +Added +~~~~~~ + +- Send scanner metadata as part of every HTTP request. Closes: #28741 +- scanner: log backtrace when not progressing. Closes: 28932 + +v1.0.2 (2018-11-10) +------------------- + +Fixed +~~~~~ + +- Update bandwidth file specification version in the ``generator`` + (#28366). +- Use 5 "=" characters as terminator in the bandwidth files (#28379) + +Changed +~~~~~~~ + +- Include the headers about eligible relays in all the bandwidth files, + not only in the ones that does not have enough eligible relays + (#28365). + +v1.0.1 (2018-11-01) +------------------- + +Changed +~~~~~~~ + +- Change default directories when sbws is run from a system service + (#28268). + +v1.0.0 (2018-10-29) +------------------- + +**Important changes**: + +- ``generate`` includes extra statistics header lines when the number + of eligible relays to include is less than the 60% of the network. It + does not include the relays' lines. +- Speed up ``scanner`` by disabling RTT measurements and waiting for + measurement threads before prioritizing again the list of relays to + measure. + +Fixed +~~~~~ + +- Update python minimal version in setup (#28043) +- Catch unhandled exception when we fail to resolve a domain name + (#28141) +- Bandwidth filtered is the maximum between the bandwidth measurements + and their mean, not the minimum (#28215) +- Stop measuring the same relay by two threads(#28061) + +Changed +~~~~~~~ + +- Move ``examples/`` to ``docs/`` (#28040) +- Number of results comparison and number of results away from each + other are incorrect (#28041) +- Stop removing results that are not away from some other X secs + (#28103) +- Use secs-away when provided instead of data\_period (#28105) +- Disable measuring RTTs (#28159) +- Rename bandwidth file keyvalues (#28197) + +Added +----- + +- Write bw file only when the percentage of measured relays is bigger + than 60% (#28062) +- When the percentage of measured relays is less than the 60%, do not + include the relays in the bandwidth file and instead include some + statistics in the header (#28076) +- When the percentage of measured relays is less than the 60% and it + was more before, warn about it (#28155) +- When the difference between the total consensus bandwidth and the + total in the bandwidth lines is larger than 50%, warn (#28216) +- Add documentation about how the bandwidth measurements are selected + and scaled before writing them to the Bandwidth File (#27692) + +v0.8.0 (2018-10-08) +------------------- + +**Important changes**: + +- Implement Torflow scaling/aggregation to be able to substitute + Torflow with sbws without affecting the bandwidth files results. +- Change stem dependency to 1.7.0, which removes the need for + ``dependency_links`` +- Update and cleanup documentation + +Added +~~~~~ + +- Add system physical requirements section to INSTALL (#26937) +- Warn when there is not enough disk space (#26937) +- Implement Torflow scaling (#27108) +- Create methods to easy graph generation and obtain statistics to + compare with current torflow results.(#27688) +- Implement rounding bw in bandwidth files to 2 insignificant + digits(#27337) +- Filter results in order to include relays in the bandwidth file + that:(#27338) +- have at least two measured bandwidths +- the measured bandwidths are within 24 hours of each other +- have at least two descriptor observed bandwidths +- the descriptor observed bandwidths are within 24 hours of each other + +Fixed +~~~~~ + +- Broken environment variable in default sbws config. To use envvar + $FOO, write $$FOO in the config. +- Stop using directory as argument in integration tests (#27342) +- Fix typo getting configuration option to allow logging to file + (#27960) +- Set int type to new arguments that otherwise would be string (#27918) +- Stop printing arguments default values, since they are printed by + default (#27916) +- Use dash instead of underscore in new cli argument names (#27917) + +Changed +~~~~~~~ + +- sbws install doc is confusing (#27341) +- Include system and Python dependencies in ``INSTALL``. +- Include dependencies for docs and tests in ``INSTALL``. +- Point to ``DEPLOY`` to run sbws. +- Remove obsolete sections in ``INSTALL`` +- Simplify ``DEPLOY``, reuse terms in the ``glossary``. +- Remove obsolete ``sbws init`` from ``DEPLOY``. +- Point to config documentation. +- Add, unify and reuse terms in ``glossary``. +- refactor v3bwfile (#27386): move scaling method inside class +- use custom ``install_command`` to test installation commands while + ``dependency_links`` is needed until #26914 is fixed. (#27704) +- documentation cleanup (#27773) +- split, merge, simplify, extend, reorganize sections and files +- generate scales as Torflow by default (#27976) +- Replace stem ``dependency_links`` by stem 1.7.0 (#27705). This also + eliminates the need for custom ``install_command`` in tox. + +v0.7.0 (2018-08-09) +------------------- + +**Important changes**: + +- ``cleanup/stale_days`` is renamed to + ``cleanup/data_files_compress_after_days`` +- ``cleanup/rotten_days`` is renamed to + ``cleanup/data_files_delete_after_days`` +- sbws now takes as an argument the path to a config file (which + contains ``sbws_home``) instead of ``sbws_home`` (which contains the + path to a config file) + +Added +~~~~~ + +- Log line on start up with sbws version, platform info, and library + versions (trac#26751) +- Manual pages (#26926) + +Fixed +~~~~~ + +- Stop deleting the latest.v3bw symlink. Instead, do an atomic rename. + (#26740) +- State file for storing the last time ``sbws scanner`` was started, + and able to be used for storing many other types of state in the + future. (GH#166) +- Log files weren't rotating. Now they are. (#26881) + +Changed +~~~~~~~ + +- Remove test data v3bw file and generate it from the same test. + (#26736) +- Stop using food terms for cleanup-related config options +- cleanup command now cleans up old v3bw files too (#26701) +- Make sbws more compatible with system packages: (#26862) +- Allow a configuration file argument +- Remove directory argument +- Create minimal user configuration when running +- Do not require to run a command to initialize +- Initialize directories when running +- Do not require configuration file inside directories specified by the + configuration + +v0.6.0 (2018-07-11) +------------------- + +**Important changes**: + +- The way users configure logging has changed. No longer are most users + expected to be familiar with how to configure python's standard + logging library with a config file. Instead we've abstracted out the + setting of log level, format, and destinations to make these settings + more accessible to users. Expert users familiar with `the logging + config file + format `__ + can still make tweaks. + +Summary of changes: + +- Make logging configuration easier for the user. +- Add UML diagrams to documentation. They can be found in + docs/source/images/ and regenerated with ``make umlsvg`` in docs/. + +Added +~~~~~ + +- UML diagrams to documentation. In docs/ run ``make umlsvg`` to + rebuild them. Requires graphviz to be installed.(GHPR#226) +- Add metadata to setup.py, useful for source/binary distributions. +- Add possibility to log to system log. (#26683) +- Add option to cleanup v3bw files. (#26701) + +Fixed +~~~~~ + +- Measure relays that have both Exit and BadExit as non-exits, which is + how clients would use them. (GH#217) +- Could not init sbws because of a catch-22 related to logging + configuration. Overhaul how logging is configured. (GH#186 GHPR#224) +- Call write method of V3BWFile class from the object instance. + (#26671) +- Stop calculating median on empty list .(#26666) + +Changed +~~~~~~~ + +- Remove is\_controller\_ok. Instead catch possible controller + exceptions and log them + +Removed +~~~~~~~ + +- Two parsing/plotting scripts in scripts/tools/ that can now be found + at https://github.com/pastly/v3bw-tools + +v0.5.0 (2018-06-26) +------------------- + +**Important changes**: + +- Result format changed, causing a version bump to 4. Updating sbws to + 0.5.0 will cause it to ignore results with version less than 4. + +Summary of changes: + +- Keep previously-generated v3bw files +- Allow a relay to limit its weight based on + RelayBandwidthRate/MaxAdvertisedBandwidth +- 1 CPU usage optimization +- 1 memory usage optimization + +Added +~~~~~ + +- Use a relay's {,Relay}BandwidthRate/MaxAdvertisedBandwidth as an + upper bound on the measurements we make for it. (GH#155) +- Ability to only consider results for a given relay valid if they came + from when that relay is using its most recent known IP address. + Thanks Juga. (GH#154 GHPR#199) +- Maintenance script to help us find functions that are (probably) no + longer being called. +- Integration test(s) for RelayPrioritizer (GHPR#206) +- Git/GitHub usage guidelines to CONTRIBUTING document (GH#208 + GHPR#215) + +Fixed +~~~~~ + +- Make relay priority calculations take only ~5% of the time they used + to (3s vs 60s) by using sets instead of lists when selecting + non-Authority relays. (GH#204) +- Make relay list refreshing take much less time by not allowing worker + threads to dogpile on the CPU. Before they would all start requesting + descriptors from Tor at roughly the same time, causing us to overload + our CPU core and make the process take unnecessarily long. Now we let + one thread do the work so it can peg the CPU on its own and get the + refresh done ASAP. (GH#205) +- Catch a JSON decode exception on malformed results so sbws can + continue gracefully (GH#210 GHPR#212) + +Changed +~~~~~~~ + +- Change the path where the Bandwidth List files are generated: now + they are stored in ``v3bw`` directory, named ``YYmmdd_HHMMSS.v3bw``, + and previously generated ones are kept. A ``latest.v3bw`` symlink is + updated. (GH#179 GHPR#190) +- Code refactoring in the v3bw classes and generation area +- Replace v3bw-into-xy bash script with python script to handle a more + complex v3bw file format (GH#182) + +v0.4.1 (2018-06-14) +------------------- + +Changed +~~~~~~~ + +- If the relay to measure is an exit, put it in the exit position and + choose a non-exit to help. Previously the relay to measure would + always be the first hop. (GH#181) +- Try harder to find a relay to help measure the target relay with two + changes. Essentially: (1) Instead of only picking from relays that + are 1.25 - 2.00 times faster than it by consensus weight, try (in + order) to find a relay that is at least 2.00, 1.75, 1.50, 1.25, or + v1.00 times as fast. If that fails, instead of giving up, (2) pick the + fastest relay in the network instead of giving up. This compliments + the previous change about measuring target exits in the exit + position. + +Fixed +~~~~~ + +- Exception that causes sbws to fall back to one measurement thread. We + first tried fixing something in this area with ``88fae60bc`` but + neglected to remember that ``.join()`` wants only string arguments + and can't handle a ``None``. So fix that. +- Exception when failing to get a relay's ``ed25519_master_key`` from + Tor and trying to do ``.rstrip()`` on a None. +- ``earliest_bandwidth`` being the newest bw not the oldest (thanks + juga0) +- ``node_id`` was missing the character "$" at the beginning diff -Nru sbws-1.0.2/docs/source/classes_original.puml sbws-1.1.0/docs/source/classes_original.puml --- sbws-1.0.2/docs/source/classes_original.puml 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/classes_original.puml 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,220 @@ +@startuml + +class RelayList { + stem.Controller _controller + Lock _refresh_lock + int _last_refresh + list @p relays + list @p bad_exits + list @p exits + list @p non_exits + list @p authorities + bool _need_refresh() + _init_relays() + _refresh() + list _relays_with_flag(int flag) + list _relays_without_flag(int flag) + list exits_not_bad_can_exit_to_port(int port) +} +RelayList *-- Relay +class Relay { + stem.RouterStatusEntryV3 _from_ns + stem.RelayDescriptor _from_desc + str @p nickname + str @p fingerprint + list @p flags + ExitPolicy @p exit_policy + str @p address + str @p master_key_ed25519 + int @p observed_bandwidth + int @p average_bandwidth + int @p burst_bandwidth + int @p consensus_bandwidth + int @p consensus_bandwidth_is_unmeasured + obj _from_ns(attr) + obj _from_desc(attr) + bool can_exit_to_port(int port) + bool is_exit_not_bad_allowing_port(int port) +} +class RelayPrioritizer { + int fresh_seconds + ResultDump result_dump + RelayList relay_list + bool measure_authorities + generator best_priority() +} +RelayPrioritizer *-- RelayList +RelayPrioritizer *-- ResultDump +Result ^-- ResultError +Result ^-- ResultSuccess +Result -- Destination +class Result { + Result.Relay _relay + list @p circ + str @p dest_url + str @p scanner + int @p time + str @p type + int @p version + str @p nickname + str @p fingerprint + str @p address + str @p master_key_ed25519 + int @p relay_observed_bandwidth + int @p relay_average_bandwidth + int @p relay_burst_bandwidth + int @p consensus_bandwidth + int @p consensus_bandwidth_is_unmeasured + dict to_dict() + Result from_dict(dict d) +} +Result -- Relay +Result *-- Result.Relay +class Result.Relay { + str nickname + str fingerprint + str address + str master_key_ed25519 + int observed_bandwidth + int average_bandwidth + int burst_bandwidth + int consensus_bandwidth + int consensus_bandwidth_is_unmeasured +} +class ResultError { + str @p msg +} +ResultError ^-- ResultErrorCircuit +class ResultErrorCircuit { +} +ResultError ^-- ResultErrorStream +class ResultSuccess { + list @p rtts + list @p downloads +} +ResultDump *-- Result +ResultDump -- Relay +class ResultDump { + dict data + int fresh_days + str datadir + Lock data_lock + Thread thread + Queue queue + store_result(Result result) + handle_result(Result result) + enter() + list results_for_relay(Relay relay) +} +class DestinationList { + list _rl + Destination next() + DestinationList @sm from_config(...) +} +DestinationList *-- Destination +class Destination { + str @p hostname + int @p port + str @p url + bool @p verify + bool is_usable() + Destination @sm from_config(str conf_section,int max_dl) +} +V3BWHeader -- Result +class V3BWHeader { + int timestamp + str version + str file_created + str latest_bandwidth + int num_lines + str software + str software_version + str generator_started + int number_eligible_relays + int minimum_number_eligible_relays + int number_consensus_relays + int percent_eligible_relays + int minimum_percent_eligible_relays + int @p num_lines + + V3BWHeader @cm from_results(dict results) + add_stats(**kwargs) + int @sm earliest_bandwidth_from_results(dict results) + str @sm generator_started_from_file(dict results) + int @sm latest_bandwidth_from_results(dict results) +} +V3BWLine -- Result +class V3BWLine { + int bw + str node_id + str master_key_ed25519 + str nick + int rtt + str time + int success + int error_stream + int error_circ + int error_misc + int bw_median + int bw_mean + int desc_bw_avg + int desc_bw_bur + int desc_bw_obs_last + int desc_bw_obs_mean + consensus_bandwidth + consensus_bandwidth_is_unmeasured + + int @sm bw_mean_from_results(list results) + int @sm bw_median_from_results(list results) + int @sm desc_bw_obs_last_from_results(list results) + int @sm desc_bw_obs_mean_from_results(list results) + V3BWLine @cm from_results(list results) + str @sm last_time_from_results(list results) + dict @sm result_types_from_results(list results) + list @sm results_away_each_other(list results) + list @sm results_recent_than(list results) +} +V3BWFile *-- V3BWHeader +V3BWFile *-- V3BWLine +V3BWFile -- Result +class V3BWFile { + V3BWHeader header + list bw_lines + @p info_stats + bool @p is_min_perc + int @p max_bw + int @p mean_bw + int @p median_bw + int @p min_bw + int @p num + int @p sum_bw + V3BWFile @cm from_results(dict results, ...) + list @sm bw_kb(bw_lines) + list @sm bw_sbws_scale(bw_lines) + list @sm bw_torflow_scale(bw_lines) + bool @sm is_max_bw_diff_perc_reached(bw_lines) + (dict, bool) @sm measured_progress_stats(bw_lines) + int @sm read_number_consensus_relays(str consensus_path) + (list, list, list) to_plt() + list update_progress(bw_lines, ...) + warn_if_not_accurate_enough(bw_lines, ...) + tuple to_plt(...) + write(str output) +} +CircuitBuilder *-- RelayList +CircuitBuilder -- Relay +class CircuitBuilder { + set built_circuits + RelayList relay_list + list relays + Controller controller + int build_circuit() + void close_circuit() +} +CircuitBuilder ^-- GapsCircuitBuilder + +class State { + get() +} + +@enduml \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/code_design.rst sbws-1.1.0/docs/source/code_design.rst --- sbws-1.0.2/docs/source/code_design.rst 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/code_design.rst 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,60 @@ +Code design +================= + +.. todo:: + - Link to refactor proposal. + - Change this page when refactoring is implemented. + +UML classes diagram +-------------------- + +.. image:: images/classes_original.* + :alt: UML classes diagram + +`classes_original.svg <./_images/classes_original.svg>`_ + +Packages diagram +----------------- + +.. image:: ./images/packages_sbws.* + :alt: packages diagram + +`packages_sbws.svg <./_images/packages_sbws.svg>`_ + +scanner threads +---------------- + +- `TorEventListener`: the thread that runs Tor and listens for events. +- ResultDump: the thread that get the measurement results from a queue + every second. +- `multiprocessing.ThreadPool` starts 3 independent threads: + - workers_thread + - tasks_thread + - results_thread +- measurement threads: they execute :func:`sbws.core.scanner.measure_relay` + There'll be a maximum of 3 by default. + +.. image:: images/threads.* + :alt: scanner threads + +Critical sections +----------------- + +Data types that are read or wrote from the threads. + +.. image:: images/critical_sections.* + :alt: scanner critical sections + :height: 400px + :align: center + +Call graph +-------------- + +Initialization calls to the moment where the measurement threads start. + +.. image:: images/pycallgraph.png + :alt: call graph + :height: 400px + :align: center + +`callgraph.png <./_images/pycallgraph.png>`_ diff -Nru sbws-1.0.2/docs/source/config.default.ini sbws-1.1.0/docs/source/config.default.ini --- sbws-1.0.2/docs/source/config.default.ini 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/config.default.ini 2019-03-29 13:47:26.000000000 +0000 @@ -36,6 +36,9 @@ [scanner] # A human-readable string with chars in a-zA-Z0-9 to identify your scanner nickname = IDidntEditTheSBWSConfig +# ISO 3166-1 alpha-2 country code. To be edited. +# Default to a non existing country to detect it was not edited. +country = AA # Limits on what download times are too fast/slow/etc. download_toofast = 1 download_min = 5 @@ -94,10 +97,12 @@ [logging] # Whether or not to log to a rotating file the directory paths.log_dname -to_file = no +to_file = yes # Whether or not to log to stdout to_stdout = yes # Whether or not to log to syslog +# NOTE that when sbws is launched by systemd, stdout goes to journal and +# syslog. to_syslog = no # If logging to file, how large (in bytes) should the file be allowed to get # before rotating to a new one. 10485760 is 10 MiB. If zero or number of @@ -107,14 +112,15 @@ # never rotate the log file. to_file_num_backups = 50 # Level to log at. Debug, info, warning, error, critical. -level = info -to_file_level = ${level} -to_stdout_level = ${level} -to_syslog_level = ${level} +# `level` must be set to the lower of all the handler levels. +level = debug +to_file_level = debug +to_stdout_level = info +to_syslog_level = info # Format string to use when logging -format = [%(asctime)s] [%(name)s] [%(levelname)s] %(message)s -to_file_format = ${format} +format = %(asctime)s %(module)s[%(process)s]: <%(levelname)s> %(message)s to_stdout_format = ${format} to_syslog_format = %(module)s[%(process)s]: <%(levelname)s> %(message)s # verbose formatter useful for debugging -#format = %(asctime)s %(levelname)s %(threadName)s %(filename)s:%(lineno)s - %(funcName)s - %(message)s +to_file_format = %(asctime)s %(levelname)s %(threadName)s %(filename)s:%(lineno)s - %(funcName)s - %(message)s + diff -Nru sbws-1.0.2/docs/source/config.example.ini sbws-1.1.0/docs/source/config.example.ini --- sbws-1.0.2/docs/source/config.example.ini 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/config.example.ini 2019-03-29 13:47:26.000000000 +0000 @@ -2,9 +2,16 @@ [scanner] # A human-readable string with chars in a-zA-Z0-9 to identify your scanner nickname = sbws_default +# ISO 3166-1 alpha-2 country code. To be edited. +# Default to a non existing country to detect it was not edited. +country = AA [destinations] foo = off [destinations.foo] url = https://example.com/does/not/exist.bin +# ISO 3166-1 alpha-2 country code. To be edited. +# Use ZZ if the destination URL is a domain name and it is in a CDN. +# Default to a non existing country to detect it was not edited. +country = AA diff -Nru sbws-1.0.2/docs/source/config.log.default.ini sbws-1.1.0/docs/source/config.log.default.ini --- sbws-1.0.2/docs/source/config.log.default.ini 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/config.log.default.ini 2019-03-29 13:47:26.000000000 +0000 @@ -34,7 +34,10 @@ args = ('/dev/log',) [formatter_to_stdout] +# format date as syslog and journal +datefmt = %b %d %H:%M:%S [formatter_to_file] +datefmt = %b %d %H:%M:%S [formatter_to_syslog] diff -Nru sbws-1.0.2/docs/source/config.rst sbws-1.1.0/docs/source/config.rst --- sbws-1.0.2/docs/source/config.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/config.rst 2019-03-29 13:47:26.000000000 +0000 @@ -1,7 +1,7 @@ .. _config_internal: -How sbws configuration works internally ----------------------------------------- +Internal code configuration files +================================== Sbws has two default config files it reads: on general, and one specific to logging. They all get combined internally to the same ``conf`` structure. @@ -27,15 +27,15 @@ .. _init-config: -.. literalinclude:: /examples/config.example.ini - :caption: Example config.example.ini +.. literalinclude:: /examples/sbws.example.ini + :caption: Example sbws.example.ini **No other configuration files are read.** .. _default-config: -Default Config --------------- +Default Configuration +---------------------- .. literalinclude:: config.default.ini :caption: config.default.ini diff -Nru sbws-1.0.2/docs/source/config_tor.rst sbws-1.1.0/docs/source/config_tor.rst --- sbws-1.0.2/docs/source/config_tor.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/config_tor.rst 2019-03-29 13:47:26.000000000 +0000 @@ -1,18 +1,37 @@ .. _config_tor: -sbws scanner tor configuration -------------------------------- +Internal Tor configuration for the scanner +------------------------------------------ -At the time of writing, sbws sets the following torrc options for the following -reasons when it launches Tor. You can find them in ``sbws/globals.py`` and -``sbws/util/stem.py``. +The scanner needs an specific Tor configuration. +The following options are either set when launching Tor or required when +connection to an existing Tor daemon. + +Default configuration: - ``SocksPort auto``: To proxy requests over Tor. - ``CookieAuthentication 1``: The easiest way to authenticate to Tor. -- ``LearnCircuitBuildTimeout 0``: To keep circuit build timeouts static. -- ``CircuitBuildTimeout 10``: To give up on struggling circuits sooner. - ``UseEntryGuards 0``: To avoid path bias warnings. -- ``DataDirectory ...``: To set Tor's datadirectory to be inside sbws's. -- ``PidFile ...``: To make it easier to tell if Tor is running. -- ``ControlSocket ...``: To control Tor. -- ``Log notice ...``: To know what the heck is going on. +- ``UseMicrodescriptors 0``: Because full server descriptors are needed. +- ``SafeLogging 0``: Useful for logging, since there's no need for anonymity. +- ``LogTimeGranularity 1`` +- ``ProtocolWarnings 1`` +- ``LearnCircuitBuildTimeout 0``: To keep circuit build timeouts static. + +Configuration that depends on the user configuration file: + +- ``CircuitBuildTimeout ...``: The timeout trying to build a circuit. +- ``DataDirectory ...``: The Tor data directory path. +- ``PidFile ...``: The Tor PID file path. +- ``ControlSocket ...``: The Tor control socket path. +- ``Log notice ...``: The Tor log level and path. + +Configuration that needs to be set on runtime: + +- ``__DisablePredictedCircuits 1``: To build custom circuits. +- ``__LeaveStreamsUnattached 1`` + +Currently most of the code that sets this configuration is in :func:`sbws.util.stem.launch_tor` +and the default configuration is ``sbws/globals.py``. + +.. note:: the location of these code is being refactored. \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/conf.py sbws-1.1.0/docs/source/conf.py --- sbws-1.0.2/docs/source/conf.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/conf.py 2019-03-29 13:47:26.000000000 +0000 @@ -49,7 +49,8 @@ 'sphinx.ext.coverage', 'sphinx.ext.githubpages', 'sphinx.ext.imgmath', - 'sphinx.ext.intersphinx' + 'sphinx.ext.intersphinx', + 'sphinx.ext.viewcode' ] # Add any paths that contain templates here, relative to this directory. @@ -199,3 +200,5 @@ source_parsers = { '.md': 'recommonmark.parser.CommonMarkParser', } + +numfig = True diff -Nru sbws-1.0.2/docs/source/contributing.rst sbws-1.1.0/docs/source/contributing.rst --- sbws-1.0.2/docs/source/contributing.rst 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/contributing.rst 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,297 @@ +.. _contributing: + +Contributing to Simple Bandwidth Scanner +========================================= + +Thank you for your interest in Simple Bandwidth Scanner (``sbws``). + +Examples of contributions include: + +* Bug reports, feature requests +* Code/documentation patches + +Bug reports or feature requests +--------------------------------- + +* Check that it has not been already reported. + +.. _ticket-ref: + +* Open a ticket in + `Tor Project Trac `_ + and assign the component to ``Core Tor``/``sbws``. + +Code/documentation patches +--------------------------- + +The sbws canonical repository is https://gitweb.torproject.org/sbws.git, +but we review patches using the Github canonical repository +(https://github.com/torproject/sbws) Pull Requests (PR). + +To know more about ``sbws`` code, + +.. seealso:: + + - :ref:`dev_doc` + - ``./docs/source/testing.rst`` (or `testing `_ + or :ref:`testing`). + - ``./docs/source/documenting.rst`` (or `documenting `_ + or :ref:`documenting`). + +The following are guidelines we aim to follow. + +Steps to create a PR +~~~~~~~~~~~~~~~~~~~~~ + +1. Create a ticket in Tor Project Trac (:ref:`Open ticket `) +2. Clone ``sbws`` via the Github web interface + https://github.com/torproject/sbws +3. Clone the repository locally +4. Install ``sbws`` as explained in ./INSTALL.rst and ./TESTING.rst + Use ``pip install -e <>`` +5. If needed install the documentation and build it as explained in + ./DOCUMENTATION.rst +6. Create a new branch, named ``ticketXXX``. + Optionally, name it with a string explaining what it does, + ie ``ticketXXX_contributing`` +7. Write code (:ref:`codestyle-ref`), tests, documentation, + extra files (:ref:`extrafiles-ref`), commit (:ref:`commits-ref`), etc. +8. Ensure tests pass (./TESTING.rst). +9. Push your branch to your repository. If you have an account in Travis, + you can see whether it pass the tests in Github and in + https://travis-ci.org/youruser/sbws/ +10. Create a PR from your branch to https://github.com/torproject/sbws +11. Change the Trac ticket status to ``needs_review`` + +.. _codestyle-ref: + +Code style +~~~~~~~~~~ + +Follow the Zen of Python (:pep:`20`) + +.. code-block:: pycon + + >>> import this + The Zen of Python, by Tim Peters + + Beautiful is better than ugly. + Explicit is better than implicit. + Simple is better than complex. + Complex is better than complicated. + Flat is better than nested. + Sparse is better than dense. + Readability counts. + +Code should adhere to the :pep:`8` guidelines. +Before release 1.0.0, some guidelines have not been followed, +such as the ordering the inputs (:pep:`8#imports`). + +External link: `Code Style `_ + +All functions, methods and classes should have :pep:`0257` +(except ``__repr__`` and ``__str__``). +Before release 1.0.0, some docstrigs do not have 3 double quotes ``"""`` +(:pep:`0257#id15`). + +External link: `Documentation `_ + +New features should add a corresponding documentation in /docs. + +An editor compatible with `EditorConfig `_ will +help you to follow the general formatting code style. + +Timestamps must be in UTC. It is prefered to use ``datetime`` objects or +Unix timestamps. Timestamps read by the user should be always formatted in +`ISO 8601 `_ + +Functional style is prefered: + +- use list comprenhensions lambda, map, reduce +- avoid reasigigning variables, instead create new ones +- use ``deepcopy`` when passing list of objects to a function/method +- classes should change attributes only in one method (other than __init__?) + +[FUNC]_ + +In general, do not reinvent the wheel, use Python native modules as ``logging``, +instead of implementing similar functionality. +Or use other packages when the new dependency can be extra, for instance +`vulture`_. + +.. _`extrafiles-ref`: + +Extra required files +~~~~~~~~~~~~~~~~~~~~~ + +Any non-trivial change should contain tests. See ./TESTING.rst. +When running tests, currently ``flake8`` informs on some PEP8 errors/warnings, +but not all. + +.. _commits-ref: + +Commits +~~~~~~~~~ + +Each commit should reference the Tor Project Trac ticket (example: ``#12345``) +and possibly the bugfix version. + +Try to make each commit a logically separate changes.:: + + As a general rule, your messages should start with a single line that’s + o more than about 50 characters and that describes the changeset concisely, + followed by a blank line, followed by a more detailed explanation. + The Git project requires that the more detailed explanation include + your motivation for the change and contrast its implementation with + previous behavior — this is a good guideline to follow. + It’s also a good idea to use the imperative present tense in these messages. + In other words, use commands. + Instead of "I added tests for" or "Adding tests for," use "Add tests for." + +[DIST]_ + +Template originally written by `Tim Pope`_: :ref:`example commit ` + +Code being reviewed workflow +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When a PR is being reviewed, new changes might be needed: + +- If the change does not modify a previous change, create new commits and push. +- If the change modifies a previous change and it's small, + `git commit fixup `_ + should be used. When it is agreed that the PR is ready, create a new branch + named ``mybranch_02`` and run: + + .. code-block:: bash + + rebase --autosquash + + push, create new PR and close old PR mentioning the number of the new PR. +- If the review takes long and when it's ready code related to the PR has changed + in master, create a new branch named ``mybranch_02`` and run: + + .. code-block:: bash + + rebase master + + push, create new PR and close old PR mentioning the number of the new PR. + +[MERG]_ + +.. _review-ref: + +Reviewing code +---------------- + +All code should be peer-reviewed. Two reasons for this are:: + + Because a developer cannot think of everything at once; + Because a fresh pair of eyes may spot an error, a corner-case in the code, + insufficient documentation, a missing consistency check, etc. + +[REVI]_ + +Reviewers: + +- Should let the contributor know what to improve/change. +- Should not push code to the contributor's branch. +- Should wait for contributor's changes or feedback after changes are requested, + before merging or closing a PR. +- Should merge (not rebase) the PR. +- If rebase is needed due to changes in master, the contributor should create + a new branch named `xxx_rebased` based on the reviewed branch, rebase and + create a new PR from it, as explained above. +- If new changes are needed when the contributor's branch is ready to merge, + the reviewer can create a new branch based on the contributor's branch, + push the changes and merge that PR. + The contributor should be notified about it. +- If the reviewer realize that new changes are needed after the PR has been + merged, the reviewer can push to master, notifying the contributor about the + changes. +- Because currently there are not many reviewers, reviewers can merge their own + PR if there was not any feedback after a week. +- Should not push directly to master, unless changes are trivial (typos, + extra spaces, etc.) +- Should not push to master new features while there are open PRs to review. + +Currently, the reviewers are the persons that have contributed to the code: +pastly, teor, juga. + +.. _releases-ref: + +Releases +---------- + +Releases follow `semantic versioning`_. +Until release 1.0.0 is reached, this project is not considered production +ready. + +Currently development happens in master, this might change from release 1.0.0 + +so that master has the last release changes, and development happens in the +next release branch. + +Before major releases, ensure that: + +- Installation from scratch, as specified in ./INSTALL.md, must success. +- All tests must pass. +- Tor must be able to parse the produced bw files + (current way is manual) + + .. todo:: + + Test that run Tor as dirauth and parse the files + +- Bandwidth files must produce graphs compatible with Torflow + (current way to test it is manual) + + .. todo:: + + Implement something to compare error with current consensus. +- A dirauth should be able to understand the documentation, otherwise the + documentation should be clarified. + +.. _changelog: + +Create a ./CHANGELOG.rst file. +Each entry should reference the Tor Project Trac ticket (example: ``#12345``) +and possibly the bugfix version. +Until version 1.0.2 we have followed `keep a changelog`_ format. + +.. _commit-msg: + +Example commit message +----------------------- + +:: + + Short (50 chars or less) summary of changes + + More detailed explanatory text, if necessary. Wrap it to + about 72 characters or so. In some contexts, the first + line is treated as the subject of an email and the rest of + the text as the body. The blank line separating the + summary from the body is critical (unless you omit the body + entirely); tools like rebase can get confused if you run + the two together. + + Further paragraphs come after blank lines. + + - Bullet points are okay, too + + - Typically a hyphen or asterisk is used for the bullet, + preceded by a single space, with blank lines in + between, but conventions vary here + + +.. rubric:: External eferences + +.. [DIST] https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project +.. [MERG] https://www.atlassian.com/git/tutorials/merging-vs-rebasing +.. [REVI] https://doc.sagemath.org/html/en/developer/reviewer_checklist.html +.. [FUNC] https://medium.com/@rohanrony/functional-programming-in-python-1-lambda-map-filter-reduce-zip-8739ea144186 +.. _tim pope: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +.. _`keep a changelog`: https://keepachangelog.com/en/1.0.0/ +.. _`semantic versioning`: https://semver.org/ +.. _`vulture`: https://pypi.org/project/vulture/ diff -Nru sbws-1.0.2/docs/source/CONTRIBUTING.rst sbws-1.1.0/docs/source/CONTRIBUTING.rst --- sbws-1.0.2/docs/source/CONTRIBUTING.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/CONTRIBUTING.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,292 +0,0 @@ -.. _contributing: - -Contributing to Simple Bandwidth Scanner -========================================= - -Thank you for your interest in Simple Bandwidth Scanner (``sbws``). - -Examples of contributions include: - -* Bug reports, feature requests -* Code/documentation patches - -Bug reports or feature requests ---------------------------------- - -* Check that it has not been already reported. - -.. _ticket-ref: - -* Open a ticket in - `Tor Project Trac `_ - and assign the component to ``Core Tor``/``sbws``. - -Code/documentation patches ---------------------------- - -The sbws canonical repository is https://gitweb.torproject.org/sbws.git, -but we review patches using the Github canonical repository -(https://github.com/torproject/sbws) Pull Requests (PR). - -To know more about ``sbws`` code, - -.. seealso:: - - - :ref:`dev_doc` - - ``./docs/source/testing.rst`` (or `testing `_ - or :ref:`testing`). - - ``./docs/source/documenting.rst`` (or `documenting `_ - or :ref:`documenting`). - -The following are guidelines we aim to follow. - -Steps to create a PR -~~~~~~~~~~~~~~~~~~~~~ - -1. Create a ticket in Tor Project Trac (:ref:`Open ticket `) -2. Clone ``sbws`` via the Github web interface - https://github.com/torproject/sbws -3. Clone the repository locally -4. Install ``sbws`` as explained in ./INSTALL.rst and ./TESTING.rst - Use ``pip install -e <>`` -5. If needed install the documentation and build it as explained in - ./DOCUMENTATION.rst -6. Create a new branch, named ``ticketXXX``. - Optionally, name it with a string explaining what it does, - ie ``ticketXXX_contributing`` -7. Write code (:ref:`codestyle-ref`), tests, documentation, - extra files (:ref:`extrafiles-ref`), commit (:ref:`commits-ref`), etc. -8. Ensure tests pass (./TESTING.rst). -9. Push your branch to your repository. If you have an account in Travis, - you can see whether it pass the tests in Github and in - https://travis-ci.org/youruser/sbws/ -10. Create a PR from your branch to https://github.com/torproject/sbws -11. Change the Trac ticket status to ``needs_review`` - -.. _codestyle-ref: - -Code style -~~~~~~~~~~ - -Follow the Zen of Python (:pep:`20`) - -.. code-block:: pycon - - >>> import this - The Zen of Python, by Tim Peters - - Beautiful is better than ugly. - Explicit is better than implicit. - Simple is better than complex. - Complex is better than complicated. - Flat is better than nested. - Sparse is better than dense. - Readability counts. - -Code should adhere to the :pep:`8` guidelines. -Before release 1.0.0, some guidelines have not been followed, -such as the ordering the inputs (:pep:`8#imports`). - -External link: `Code Style `_ - -All functions, methods and classes should have :pep:`0257` -(except ``__repr__`` and ``__str__``). -Before release 1.0.0, some docstrigs do not have 3 double quotes ``"""`` -(:pep:`0257#id15`). - -External link: `Documentation `_ - -New features should add a corresponding documentation in /docs. - -An editor compatible with `EditorConfig `_ will -help you to follow the general formatting code style. - -Timestamps must be in UTC. It is prefered to use ``datetime`` objects or -Unix timestamps. Timestamps read by the user should be always formatted in -`ISO 8601 `_ - -Functional style is prefered: - -- use list comprenhensions lambda, map, reduce -- avoid reasigigning variables, instead create new ones -- use ``deepcopy`` when passing list of objects to a function/method -- classes should change attributes only in one method (other than __init__?) - -[FUNC]_ - -In general, do not reinvent the wheel, use Python native modules as ``logging``, -instead of implementing similar functionality. -Or use other packages when the new dependency can be extra, for instance -`vulture`_. - -.. _`extrafiles-ref`: - -Extra required files -~~~~~~~~~~~~~~~~~~~~~ - -Any non-trivial change should contain tests. See ./TESTING.rst. -When running tests, currently ``flake8`` informs on some PEP8 errors/warnings, -but not all. - -Document your changes in ./CHANGELOG.rst following `keep a changelog`_. -Reference the Tor Project Trac ticket (example: ``#12345``) or -Github ticket (example: ``GH#123``). - -.. _commits-ref: - -Commits -~~~~~~~~~ - -Try to make each commit a logically separate changes.:: - - As a general rule, your messages should start with a single line that’s - o more than about 50 characters and that describes the changeset concisely, - followed by a blank line, followed by a more detailed explanation. - The Git project requires that the more detailed explanation include - your motivation for the change and contrast its implementation with - previous behavior — this is a good guideline to follow. - It’s also a good idea to use the imperative present tense in these messages. - In other words, use commands. - Instead of "I added tests for" or "Adding tests for," use "Add tests for." - -[DIST]_ - -Template originally written by `Tim Pope`_: :ref:`example commit ` - -Code being reviewed workflow -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When a PR is being reviewed, new changes might be needed: - -- If the change does not modify a previous change, create new commits and push. -- If the change modifies a previous change and it's small, - `git commit fixup `_ - should be used. When it is agreed that the PR is ready, create a new branch - named ``mybranch_02`` and run: - - .. code-block:: bash - - rebase --autosquash - - push, create new PR and close old PR mentioning the number of the new PR. -- If the review takes long and when it's ready code related to the PR has changed - in master, create a new branch named ``mybranch_02`` and run: - - .. code-block:: bash - - rebase master - - push, create new PR and close old PR mentioning the number of the new PR. - -[MERG]_ - -.. _review-ref: - -Reviewing code ----------------- - -All code should be peer-reviewed. Two reasons for this are:: - - Because a developer cannot think of everything at once; - Because a fresh pair of eyes may spot an error, a corner-case in the code, - insufficient documentation, a missing consistency check, etc. - -[REVI]_ - -Reviewers: - -- Should let the contributor know what to improve/change. -- Should not push code to the contributor's branch. -- Should wait for contributor's changes or feedback after changes are requested, - before merging or closing a PR. -- Should merge (not rebase) the PR. -- If rebase is needed due to changes in master, the contributor should create - a new branch named `xxx_rebased` based on the reviewed branch, rebase and - create a new PR from it, as explained above. -- If new changes are needed when the contributor's branch is ready to merge, - the reviewer can create a new branch based on the contributor's branch, - push the changes and merge that PR. - The contributor should be notified about it. -- If the reviewer realize that new changes are needed after the PR has been - merged, the reviewer can push to master, notifying the contributor about the - changes. -- Because currently there are not many reviewers, reviewers can merge their own - PR if there was not any feedback after a week. -- Should not push directly to master, unless changes are trivial (typos, - extra spaces, etc.) -- Should not push to master new features while there are open PRs to review. - -Currently, the reviewers are the persons that have contributed to the code: -pastly, teor, juga. - -.. _releases-ref: - -Releases ----------- - -Releases follow `semantic versioning`_. -Until release 1.0.0 is reached, this project is not considered production -ready. - -Currently development happens in master, this might change from release 1.0.0 - -so that master has the last release changes, and development happens in the -next release branch. - -Before major releases, ensure that: - -- Installation from scratch, as specified in ./INSTALL.md, must success. -- All tests must pass. -- Tor must be able to parse the produced bw files - (current way is manual) - - .. todo:: - - Test that run Tor as dirauth and parse the files - -- Bandwidth files must produce graphs compatible with Torflow - (current way to test it is manual) - - .. todo:: - - Implement something to compare error with current consensus. -- A dirauth should be able to understand the documentation, otherwise the - documentation should be clarified. - - -.. _commit-msg: - -Example commit message ------------------------ - -:: - - Short (50 chars or less) summary of changes - - More detailed explanatory text, if necessary. Wrap it to - about 72 characters or so. In some contexts, the first - line is treated as the subject of an email and the rest of - the text as the body. The blank line separating the - summary from the body is critical (unless you omit the body - entirely); tools like rebase can get confused if you run - the two together. - - Further paragraphs come after blank lines. - - - Bullet points are okay, too - - - Typically a hyphen or asterisk is used for the bullet, - preceded by a single space, with blank lines in - between, but conventions vary here - - -.. rubric:: External eferences - -.. [DIST] https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project -.. [MERG] https://www.atlassian.com/git/tutorials/merging-vs-rebasing -.. [REVI] https://doc.sagemath.org/html/en/developer/reviewer_checklist.html -.. [FUNC] https://medium.com/@rohanrony/functional-programming-in-python-1-lambda-map-filter-reduce-zip-8739ea144186 -.. _tim pope: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html -.. _`keep a changelog`: https://keepachangelog.com/en/1.0.0/ -.. _`semantic versioning`: https://semver.org/ -.. _`vulture`: https://pypi.org/project/vulture/ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/data/critical_sections.dia and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/data/critical_sections.dia differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/data/scanner.dia and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/data/scanner.dia differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/data/use_cases_data_sources.dia and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/data/use_cases_data_sources.dia differ diff -Nru sbws-1.0.2/docs/source/DEPLOY.rst sbws-1.1.0/docs/source/DEPLOY.rst --- sbws-1.0.2/docs/source/DEPLOY.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/DEPLOY.rst 2019-03-29 13:47:26.000000000 +0000 @@ -19,18 +19,42 @@ - A Web server installed and running that supports HTTP GET, HEAD and Range (:rfc:`7233`) requests. ``Apache`` HTTP Server and ``Nginx`` support them. -- Optional support for TLS +- TLS support to avoid HTTP content caches at the various exit nodes. +- Certificates can be self-signed. - A large file; at the time of writing, at least 1 GiB in size + It can be created running:: + + head -c $((1024*1024*1024)) /dev/urandom > 1GiB + +- A fixed IP address or a domain name. +- Bandwidth: at least 12.5MB/s (100 Mbit/s). +- Network traffic: around 12-15GB/day. scanner setup ---------------------- -Install sbws according to ``./INSTALL.rst`` (or `/INSTALL.rst `_ -or :ref:`install`). +Install sbws according to ``_ (in the local directory or GitHub) +or ``_ (local build or Read the Docs). + +To run the ``scanner`` it is mandatory to create a configuration file with at +least one ``destination``. +It is recommended to set several ``destination``s so that the ``scanner`` can +continue if one fails. + +If ``sbws`` is installed from the Debian package, then create a file in +``/etc/sbws/sbws.ini`` like in the following example: + +.. literalinclude:: /examples/sbws.example.ini + :caption: Example sbws.example.ini + +If ``sbws`` is installed from the sources as a non-root user then create the +file in ``~/.sbws.ini``. -To configure destinations, create a configuration file according to -``./docs/source/man_sbws.ini.rst`` (or `/docs/source/man_sbws.ini.rst `_ -or :doc:`man_sbws.ini` or ``man sbws.ini``) +More details about the configuration file can be found in +``./docs/source/man_sbws.ini.rst`` (in the local directory or GitHub) or +``_ (local build or Read the Docs) or +``man sbws.ini`` (system package). -See also ``./docs/source/man_sbws.rst`` (or `/docs/source/man_sbws.rst `_ or -:doc:`man_sbws` or ``man sbws``) manual page. +See also ``./docs/source/man_sbws.rst`` (in the local directory or GitHub) or +``_ (local build or Read the Docs) or ``man sbws`` (system +package). diff -Nru sbws-1.0.2/docs/source/diagrams.rst sbws-1.1.0/docs/source/diagrams.rst --- sbws-1.0.2/docs/source/diagrams.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/diagrams.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -UML diagrams -============= - -Class Diagram --------------------- - -.. image:: ./images/classes_sbws.* - -`classes_sbws.svg <./_images/classes_sbws.svg>`_ - -Packages diagram ------------------ - -.. image:: ./images/packages_sbws.* - -`packages_sbws.svg <./_images/packages_sbws.svg>`_ \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/documenting.rst sbws-1.1.0/docs/source/documenting.rst --- sbws-1.0.2/docs/source/documenting.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/documenting.rst 2019-03-29 13:47:26.000000000 +0000 @@ -1,7 +1,7 @@ .. _documenting: -Installing documentation dependendencies and building it ---------------------------------------------------------- +Installing and building the documentation +----------------------------------------- To build the documentation, extra Python dependencies are needed: @@ -35,13 +35,15 @@ are needed: - Core and Extra Tex_ Live packages +- dvipng_ They are included in most distributions. In Debian install them running:: - apt install texlive-latex-extra + apt install texlive-latex-extra dvpipng -.. _Sphinx: http://www.sphinx-doc.org +.. _Sphinx: https://www.sphinx-doc.org .. _recommonmark: https://recommonmark.readthedocs.io/ .. _Pylint: https://www.pylint.org/ -.. _Tex: http://www.tug.org/texlive/acquire.html +.. _Tex: https://www.tug.org/texlive/acquire.html +.. _dvipng: https://www.nongnu.org/dvipng/ \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/examples/config.example.ini sbws-1.1.0/docs/source/examples/config.example.ini --- sbws-1.0.2/docs/source/examples/config.example.ini 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/examples/config.example.ini 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -# Minimum configuration that needs to be customized -[scanner] -# A human-readable string with chars in a-zA-Z0-9 to identify your scanner -nickname = sbws_default - -[destinations] -foo = off - -[destinations.foo] -url = https://example.com/does/not/exist.bin diff -Nru sbws-1.0.2/docs/source/examples/sbws.example.ini sbws-1.1.0/docs/source/examples/sbws.example.ini --- sbws-1.0.2/docs/source/examples/sbws.example.ini 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/examples/sbws.example.ini 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,60 @@ +# Minimum configuration that needs to be customized +[scanner] +# A human-readable string with chars in a-zA-Z0-9 to identify your scanner +nickname = sbws_default +# ISO 3166-1 alpha-2 country code where the Web server destination is located. +# Default AA, to detect it was not edited. +country = SN + +[destinations] +# With several destinations, the scanner can continue even if some of them +# fail, which can be caused by a network problem on their side. +# If all of them fail, the scanner will stop, which +# will happen if there is network problem on the scanner side. + +# A destination can be disabled changing `on` by `off` +foo = on + +[destinations.foo] +# the domain and path to the 1GB file. +url = https://example.com/does/not/exist.bin +# Whether to verify or not the TLS certificate. Default True +verify = False +# ISO 3166-1 alpha-2 country code where the Web server destination is located. +# Default AA, to detect it was not edited. +# Use ZZ if the location is unknown (for instance, a CDN). +country = ZZ + +# Number of consecutive times that a destination could not be used to measure +# before stopping to try to use it for a while that by default is 3h. +max_num_failures = 3 + +## The following logging options are set by default. +## There is no need to change them unless other options are prefered. +; [logging] +; # Whether or not to log to a rotating file the directory paths.log_dname +; to_file = yes +; # Whether or not to log to stdout +; to_stdout = yes +; # Whether or not to log to syslog +; # NOTE that when sbws is launched by systemd, stdout goes to journal and +; # syslog. +; to_syslog = no + +; # Level to log at. Debug, info, warning, error, critical. +; # `level` must be set to the lower of all the handler levels. +; level = debug +; to_file_level = debug +; to_stdout_level = info +; to_syslog_level = info +; # Format string to use when logging +; format = %(module)s[%(process)s]: <%(levelname)s> %(message)s +; # verbose formatter useful for debugging +; to_file_format = %(asctime)s %(levelname)s %(threadName)s %(filename)s:%(lineno)s - %(funcName)s - %(message)s +; # Not adding %(asctime)s to to stdout since it'll go to syslog when using +; # systemd, and it'll have already the date. +; to_stdout_format = ${format} +; to_syslog_format = ${format} + +# To disable certificate validation, uncomment the following +# verify = False \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/how_works.rst sbws-1.1.0/docs/source/how_works.rst --- sbws-1.0.2/docs/source/how_works.rst 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/how_works.rst 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,165 @@ +What the scanner and the generator do +====================================== + +Running the scanner +----------------------- + +Overview +~~~~~~~~~ + +.. The following text is part of the introduction in the README, but rst + formatted. + +The :term:`scanner` measures the bandwidth of each relay in the Tor network +(except the directory authorities) by creating a two hops circuit +with the relay. It then measures the bandwidth by downloading data +from a :term:`destination` Web Server and stores the measurements. + +The :term:`generator` read the measurements, aggregates, filters and +scales them using torflow's scaling method. + +Then it generates a :term:`bandwidth list file` that is read +by a :term:`directory authority` to report relays’ bandwidth in its vote. + +.. image:: ./images/scanner.svg + :height: 200px + :align: center + +Intialization +~~~~~~~~~~~~~~ + +.. At some point it should be able to get environment variables + +#. Parse the command line arguments and configuration files. +#. Launch a Tor thread with an specific configuration or connect to a running + Tor daemon that is running with a suitable configuration. +#. Obtain the list of relays in the Tor network from the Tor consensus and + descriptor documents. +#. Read and parse the old bandwidth measurements stored in the file system. +#. Select a subset of the relays to be measured next, ordered by: + + #. relays not measured. + #. measurements age. + +.. image:: ./images/use_cases_data_sources.svg + :alt: data sources + :height: 200px + :align: center + +Classes used in the initialization: + +.. image:: ./images/use_cases_classes.svg + :alt: classes initializing data + :height: 300px + :align: center + +Source code: :func:`sbws.core.scanner.run_speedtest` + +Measuring relays +~~~~~~~~~~~~~~~~~ + +#. For every relay: +#. Select a second relay to build a Tor circuit. +#. Build the circuit. +#. Make HTTPS GET requests to the Web server over the circuit. +#. Store the time the request took and the amount of bytes requested. + +.. image:: ./images/activity_all.svg + :alt: activity measuring relays + :height: 300px + :align: center + +Source code: :func:`sbws.core.scanner.measure_relay` + +Selecting a second relay +~~~~~~~~~~~~~~~~~~~~~~~~ + +#. If the relay to measure is an exit, use it as an exit and obtain the + non-exits. +#. If the relay to measure is not an exit, use it as first hop and obtain + the exits. +#. From non-exits or exits, select one randomly from the ones that have + double consensus bandwidth than the relay to measure. +#. If there are no relays that satisfy this, lower the required bandwidth. + +.. image:: ./images/activity_second_relay.svg + :alt: activity select second relay + :height: 400px + :align: center + +Source code: :func:`sbws.core.scanner.measure_relay` + +Selecting the data to download +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#. While the downloaded data is smaller than 1GB or the number of download + is minor than 5: +#. Randomly, select a 16MiB range. +#. If it takes less than 5 seconds, select a bigger range and don't keep any + information. +#. If it takes more than 10 seconds, select an smaller range and don't keep any + information. +#. Store the number of bytes downloaded and the time it took. + +Source code: :func:`sbws.core.scanner._should_keep_result` + +Writing the measurements to the filesystem +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For every measured relay, the measurement result is put in a queue. +There's an independent thread getting measurements from the queue every second. +Every new measurement is appended to a file as a json line +(but the file itself is not json!). +The file is named with the current date. Every day a new file is created. + +Source code: :func:`sbws.lib.resultdump.ResultDump.enter` + +Running the generator +----------------------- + +Every hour, the generator: +#. Aggregate all the measurements (not older than 6 six days) for every relay. +#. Filter the measurements +#. Scale the measurements +#. Write the bandwidth file + +Source code: :func:`sbws.lib.v3bwfile.V3BWFile.from_results` + +Filtering the bandwidth measurements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Each relay bandwidth measurements are selected in the following way: + +#. At least two bandwidth measurements (``Result`` s) MUST have been obtained + within an arbitrary number of seconds (currently one day). + If they are not, the relay MUST NOT be included in the Bandwith File. +#. The measurements than are are older than an arbitrary number of senconds + in the past MUST be discarded. + Currently this number is the same as ``data_period`` (5 days). + +If the number of relays to include in the Bandwidth File are less than +a percententage (currently 60%) than the number of relays in the consensus, +additional Header Lines MUST be added (see XXX) to the Bandwith File and the +relays SHOULD NOT be included. + +Scaling the bandwidth measurements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Consensus bandwidth obtained by new implementations MUST be comparable to the +consensus bandwidth, therefore they MUST implement torflow_scaling_. +The bandwidth_file_spec_ appendix B describes torflow scaling and a linear +scaling method. + +Writing the bandwidth file +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The bandwidth file format is defined in the bandwidth_file_spec_. + + +.. _torflow: https://gitweb.torproject.org/torflow.git +.. _stem: https://stem.torproject.org +.. https://github.com/requests/requests/issues/4885 +.. _requests: http://docs.python-requests.org/ +.. _peerflow: https://www.nrl.navy.mil/itd/chacs/sites/www.nrl.navy.mil.itd.chacs/files/pdfs/16-1231-4353.pdf +.. _torflow_scaling: https://gitweb.torproject.org/torflow.git/tree/NetworkScanners/BwAuthority/README.spec.txt#n298 +.. _bandwidth_file_spec: https://gitweb.torproject.org/torspec.git/tree/bandwidth-file-spec.txt \ No newline at end of file Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/images/20180901_163442.png and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/images/20180901_163442.png differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/images/20180901_164014.png and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/images/20180901_164014.png differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/images/43710932-ac1eeea8-9960-11e8-9e7e-21fddff2f7a3.png and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/images/43710932-ac1eeea8-9960-11e8-9e7e-21fddff2f7a3.png differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/images/43710933-ac95e0bc-9960-11e8-9aaf-0bb1f83b65e2.png and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/images/43710933-ac95e0bc-9960-11e8-9aaf-0bb1f83b65e2.png differ diff -Nru sbws-1.0.2/docs/source/images/activity_all.svg sbws-1.1.0/docs/source/images/activity_all.svg --- sbws-1.0.2/docs/source/images/activity_all.svg 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/images/activity_all.svg 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +Select a destinationSelect a second relayBuild a circuitHTTP GET (Range-Bytes)Store measurementnext relay to measure?no SIGINT/SIGTERM? \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/images/activity_second_relay.svg sbws-1.1.0/docs/source/images/activity_second_relay.svg --- sbws-1.0.2/docs/source/images/activity_second_relay.svg 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/images/activity_second_relay.svg 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +relay to measure is exit?yesnoobtain non-exitsobtain an exitswithout bad flagthat can exitto port 443potential second relaysobtain a relayfrom potentialsencond relaysrandomlyyessecond relay has 2x bandwidth?yesother second relay has 1.5x bandwidth?yesother second relay has 1x bandwidth?nothingsecond relay selected!Build a circuitwhith exit assecond hop \ No newline at end of file Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/images/advertised_bandwidth.png and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/images/advertised_bandwidth.png differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/images/bwauth_measured_7days.png and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/images/bwauth_measured_7days.png differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/images/bwauth_measured_90days.png and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/images/bwauth_measured_90days.png differ diff -Nru sbws-1.0.2/docs/source/images/bwauth.svg sbws-1.1.0/docs/source/images/bwauth.svg --- sbws-1.0.2/docs/source/images/bwauth.svg 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/images/bwauth.svg 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,1570 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + diff -Nru sbws-1.0.2/docs/source/images/classes_original.svg sbws-1.1.0/docs/source/images/classes_original.svg --- sbws-1.0.2/docs/source/images/classes_original.svg 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/images/classes_original.svg 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +RelayListstem.Controller _controllerLock _refresh_lockint _last_refreshlist @p relayslist @p bad_exitslist @p exitslist @p non_exitslist @p authoritiesbool _need_refresh()_init_relays()_refresh()list _relays_with_flag(int flag)list _relays_without_flag(int flag)list exits_not_bad_can_exit_to_port(int port)Relaystem.RouterStatusEntryV3 _from_nsstem.RelayDescriptor _from_descstr @p nicknamestr @p fingerprintlist @p flagsExitPolicy @p exit_policystr @p addressstr @p master_key_ed25519int @p observed_bandwidthint @p average_bandwidthint @p burst_bandwidthint @p consensus_bandwidthint @p consensus_bandwidth_is_unmeasuredobj _from_ns(attr)obj _from_desc(attr)bool can_exit_to_port(int port)bool is_exit_not_bad_allowing_port(int port)RelayPrioritizerint fresh_secondsResultDump result_dumpRelayList relay_listbool measure_authoritiesgenerator best_priority()ResultDumpdict dataint fresh_daysstr datadirLock data_lockThread threadQueue queuestore_result(Result result)handle_result(Result result)enter()list results_for_relay(Relay relay)ResultResult.Relay _relaylist @p circstr @p dest_urlstr @p scannerint @p timestr @p typeint @p versionstr @p nicknamestr @p fingerprintstr @p addressstr @p master_key_ed25519int @p relay_observed_bandwidthint @p relay_average_bandwidthint @p relay_burst_bandwidthint @p consensus_bandwidthint @p consensus_bandwidth_is_unmeasureddict to_dict()Result from_dict(dict d)ResultErrorstr @p msgResultSuccesslist @p rttslist @p downloadsDestinationstr @p hostnameint @p portstr @p urlbool @p verifybool is_usable()Destination @sm from_config(str conf_section,int max_dl)Result.Relaystr nicknamestr fingerprintstr addressstr master_key_ed25519int observed_bandwidthint average_bandwidthint burst_bandwidthint consensus_bandwidthint consensus_bandwidth_is_unmeasuredResultErrorCircuitResultErrorStreamDestinationListlist _rlDestination next()DestinationList @sm from_config(...)V3BWHeaderint timestampstr versionstr file_createdstr latest_bandwidthint num_linesstr softwarestr software_versionstr generator_startedint number_eligible_relaysint minimum_number_eligible_relaysint number_consensus_relaysint percent_eligible_relaysint minimum_percent_eligible_relaysint @p num_linesV3BWHeader @cm from_results(dict results)add_stats(**kwargs)int @sm earliest_bandwidth_from_results(dict results)str @sm generator_started_from_file(dict results)int @sm latest_bandwidth_from_results(dict results)V3BWLineint bwstr node_idstr master_key_ed25519str nickint rttstr timeint successint error_streamint error_circint error_miscint bw_medianint bw_meanint desc_bw_avgint desc_bw_burint desc_bw_obs_lastint desc_bw_obs_meanconsensus_bandwidthconsensus_bandwidth_is_unmeasuredint @sm bw_mean_from_results(list results)int @sm bw_median_from_results(list results)int @sm desc_bw_obs_last_from_results(list results)int @sm desc_bw_obs_mean_from_results(list results)V3BWLine @cm from_results(list results)str @sm last_time_from_results(list results)dict @sm result_types_from_results(list results)list @sm results_away_each_other(list results)list @sm results_recent_than(list results)V3BWFileV3BWHeader headerlist bw_lines@p info_statsbool @p is_min_percint @p max_bwint @p mean_bwint @p median_bwint @p min_bwint @p numint @p sum_bwV3BWFile @cm from_results(dict results, ...)list @sm bw_kb(bw_lines)list @sm bw_sbws_scale(bw_lines)list @sm bw_torflow_scale(bw_lines)bool @sm is_max_bw_diff_perc_reached(bw_lines)(dict, bool) @sm measured_progress_stats(bw_lines)int @sm read_number_consensus_relays(str consensus_path)(list, list, list) to_plt()list update_progress(bw_lines, ...)warn_if_not_accurate_enough(bw_lines, ...)tuple to_plt(...)write(str output)CircuitBuilderset built_circuitsRelayList relay_listlist relaysController controllerint build_circuit()void close_circuit()GapsCircuitBuilderStateget() \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/images/classes_sbws.svg sbws-1.1.0/docs/source/images/classes_sbws.svg --- sbws-1.0.2/docs/source/images/classes_sbws.svg 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/images/classes_sbws.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,246 +0,0 @@ - - - - - - -classes_sbws - - -0 - -CircuitBuilder - -built_circuits : set -circuit_timeout -close_circuits_on_exit : bool -controller -relay_list -relays -rng : SystemRandom - -build_circuit() -close_circuit() - - -1 - -Destination - -hostname -port -url -verify - -from_config() -is_usable() - - -2 - -DestinationList - - -from_config() -next() - - -3 - -DirectoryLock - - - - - -14 - -_FLock - - - - - -3->14 - - - - -4 - -FileLock - - - - - -4->14 - - - - -5 - -GapsCircuitBuilder - - -build_circuit() - - -5->0 - - - - -6 - -PathLengthException - -errors : NoneType - - - - -7 - -Relay - -address -average_bandwidth -bandwidth -exit_policy -fingerprint -flags -master_key_ed25519 -nickname -observed_bandwidth - -can_exit_to() - - -8 - -RelayList - -REFRESH_INTERVAL : int -authorities -bad_exits -exits -fast -guards -non_exits -relays -rng : SystemRandom - -random_relay() - - -9 - -RelayPrioritizer - -fraction_to_return -fresh_seconds -measure_authorities -min_to_return -relay_list -result_dump - -best_priority() - - -10 - -State - - -get() - - -11 - -V3BWFile - -bw_lines -header -info_stats -is_min_perc -max_bw -mean_bw -median_bw -min_bw -num -sum_bw - -bw_kb() -bw_line_for_node_id() -bw_sbws_scale() -bw_torflow_scale() -from_results() -from_v100_fpath() -from_v110_fpath() -is_max_bw_diff_perc_reached() -measured_progress_stats() -read_number_consensus_relays() -to_plt() -update_progress() -warn_if_not_accurate_enough() -write() - - -12 - -V3BWHeader - -file_created -keyvalue_tuple_ls -keyvalue_unordered_tuple_ls -keyvalue_v110str_ls -keyvalue_v200_ls -latest_bandwidth -num_lines -software -software_version -strv110 -strv200 -timestamp -version - -add_stats() -earliest_bandwidth_from_results() -from_lines_v100() -from_lines_v110() -from_results() -from_text_v110() -generator_started_from_file() -latest_bandwidth_from_results() - - -13 - -V3BWLine - -bw -bw_keyvalue_tuple_ls -bw_keyvalue_v110str_ls -bw_strv110 -node_id - -bw_mean_from_results() -bw_median_from_results() -desc_bw_obs_last_from_results() -desc_bw_obs_mean_from_results() -from_bw_line_v110() -from_data() -from_results() -last_time_from_results() -result_types_from_results() -results_away_each_other() -results_recent_than() -rtt_from_results() - - - diff -Nru sbws-1.0.2/docs/source/images/critical_sections.svg sbws-1.1.0/docs/source/images/critical_sections.svg --- sbws-1.0.2/docs/source/images/critical_sections.svg 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/images/critical_sections.svg 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,516 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/images/pycallgraph.png and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/images/pycallgraph.png differ diff -Nru sbws-1.0.2/docs/source/images/scanner.svg sbws-1.1.0/docs/source/images/scanner.svg --- sbws-1.0.2/docs/source/images/scanner.svg 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/images/scanner.svg 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,248 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru sbws-1.0.2/docs/source/images/threads.svg sbws-1.1.0/docs/source/images/threads.svg --- sbws-1.0.2/docs/source/images/threads.svg 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/images/threads.svg 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +MainThreadTorEventListenerResultDumpworkers_threadtasks_threadresutls_treadmeasure_relaythreads < 3? \ No newline at end of file Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/images/torperf.png and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/images/torperf.png differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/docs/source/images/totalcw.png and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/docs/source/images/totalcw.png differ diff -Nru sbws-1.0.2/docs/source/images/use_cases_classes.svg sbws-1.1.0/docs/source/images/use_cases_classes.svg --- sbws-1.0.2/docs/source/images/use_cases_classes.svg 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/images/use_cases_classes.svg 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,401 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru sbws-1.0.2/docs/source/images/use_cases_data_sources.svg sbws-1.1.0/docs/source/images/use_cases_data_sources.svg --- sbws-1.0.2/docs/source/images/use_cases_data_sources.svg 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/images/use_cases_data_sources.svg 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru sbws-1.0.2/docs/source/index.rst sbws-1.1.0/docs/source/index.rst --- sbws-1.0.2/docs/source/index.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/index.rst 2019-03-29 13:47:26.000000000 +0000 @@ -19,7 +19,6 @@ README INSTALL DEPLOY - CONTRIBUTING CHANGELOG AUTHORS man_sbws @@ -37,18 +36,22 @@ .. toctree:: :maxdepth: 1 - specification - faq - glossary - roadmap - layout - config_tor + contributing testing documenting + how_works + code_design state config - diagrams + config_tor sbws + bandwidth_distribution + tor_bandwidth_files + bandwidth_authorities + monitoring_bandwidth + roadmap + glossary + faq Proposals: diff -Nru sbws-1.0.2/docs/source/INSTALL.rst sbws-1.1.0/docs/source/INSTALL.rst --- sbws-1.0.2/docs/source/INSTALL.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/INSTALL.rst 2019-03-29 13:47:26.000000000 +0000 @@ -19,7 +19,7 @@ System requirements -------------------- -- Tor +- Tor (last stable version is recommended) - Python 3 (>= 3.5) - virtualenv_ (while there is not ``stem`` release > 1.6.0, it is recommended to install the required python dependencies in a virtualenv) @@ -43,7 +43,7 @@ Clone ``sbws``:: - git clone https://gitweb.torproject.org/sbws.git + git clone https://git.torproject.org/sbws.git Install the python dependencies:: @@ -51,13 +51,14 @@ ``sbws`` needs :term:`destination` s to request files from. -Please, see ./DEPLOY.rst (or `/DEPLOY.rst `_ or :ref:`deploy`) +Please, see ``_ (in the local directory or GitHub) or +``_ (local build or Read the Docs) to configure, deploy and run ``sbws``. System physical requirements ----------------------------- -- Bandwidth: at least 20MB/s (160 Mbit/s). The more the better. +- Bandwidth: at least 12.5MB/s (100 Mbit/s). - Free RAM: at least 1.5GB - Free disk: at least 3GB @@ -82,8 +83,11 @@ .. _virtualenv: https://virtualenv.pypa.io/en/stable/installation/ .. _Stem: https://stem.torproject.org/ .. _socks: http://docs.python-requests.org/en/master/user/advanced/#socks +.. https://readthedocs.org/projects/requests/ redirect to this, but the +.. certificate of this signed by rtd .. _Requests: http://docs.python-requests.org/ -.. _Flake8: http://flake8.pycqa.org/ +.. http://flake8.pycqa.org/ certificate is signed by rtf +.. _Flake8: https://flake8.readthedocs.org/ .. _pytest: https://docs.pytest.org/ .. _tox: https://tox.readthedocs.io .. _Coverage: https://coverage.readthedocs.io/ diff -Nru sbws-1.0.2/docs/source/man_sbws.ini.rst sbws-1.1.0/docs/source/man_sbws.ini.rst --- sbws-1.0.2/docs/source/man_sbws.ini.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/man_sbws.ini.rst 2019-03-29 13:47:26.000000000 +0000 @@ -6,14 +6,12 @@ Tor bandwidth scanner configuration file. -**sbws** (1) ``scanner`` command requires a configuration file with a -"[destinations]" section. +**sbws** (1) ``scanner`` command requires a configuration file with the +"[scanner]", "[destinations]" "[destination.]" sections. -It is the only section that does not have a default value. - -It is recommended, but not required to configure "nickname" in the "[scanner]" -section. +There must be at least one "[destination.]". +See an **EXAMPLES** below for a minimal configuration. SECTIONS --------- @@ -58,6 +56,11 @@ (Default ~/.sbws/log) destinations + + It is required to set at least one destination for the scanner to run. + It is recommended to set several destinations so that the scanner can + continue if one fails. + STR = {on, off} Name of destination. It is a name for the Web server from where to download files in order to measure bandwidths. @@ -69,6 +72,12 @@ url = STR The URL to the destination. It must include a file path. It can use both http or https. + verify = BOOL + Whether or not to verify the destination certificate. + (Default: True) + country = STR + ISO 3166-1 alpha-2 country code. + Use ZZ if the destination URL is a domain name and it is in a CDN. tor @@ -91,6 +100,9 @@ nickname = STR A human-readable string with chars in a-zA-Z0-9 to identify the scanner. (Default: IDidntEditTheSBWSConfig) + country = STR + ISO 3166-1 alpha-2 country code. + (Default: AA, a non existing country to detect it was not edited) download_toofast = INT Limits on what download times are too fast/slow/etc. (Default: 1) download_min = INT @@ -139,6 +151,8 @@ Whether or not to log to stdout. (Default: yes) to_syslog = {yes, no} Whether or not to log to syslog. (Default: no) + NOTE that when sbws is launched by systemd, stdout goes to journal and + syslog. to_file_max_bytes = INT If logging to file, how large (in bytes) should the file be allowed to get before rotating to a new one. 10485760 is 10 MiB. If zero or number of @@ -170,6 +184,10 @@ Example ``destinations`` section:: + [scanner] + nickname = Manual + country = US + [destinations] foo = on bar = on @@ -178,14 +196,18 @@ [destinations.foo] # using HTTP url = http://example.org/sbws.bin + country = ZZ + verify = False [destinations.bar] # using HTTPS url = https://example.com/data + country = SN [destinations.baz] # this will be ignored url = https://example.net/ask/stan/where/the/file/is.exe + country = TH FILES ----- diff -Nru sbws-1.0.2/docs/source/monitoring_bandwidth.rst sbws-1.1.0/docs/source/monitoring_bandwidth.rst --- sbws-1.0.2/docs/source/monitoring_bandwidth.rst 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/monitoring_bandwidth.rst 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,35 @@ +Monitoring bandwidth changes in the Tor Network +================================================ + +Total bandwidth +--------------- + +Should not decrease. + +.. image:: images/advertised_bandwidth.png + :alt: advertised bandwidth + + +​https://metrics.torproject.org/bandwidth-flags.html + + +Time to download a file +----------------------- + +Should not increase. + +.. image:: images/torperf.png + :alt: torperf + + +​https://metrics.torproject.org/torperf.html + + +Total consensus weights across bandwidth authorities +---------------------------------------------------- + +.. image:: images/totalcw.png + :alt: total consensus weight + + +​https://metrics.torproject.org/totalcw.html diff -Nru sbws-1.0.2/docs/source/README.md sbws-1.1.0/docs/source/README.md --- sbws-1.0.2/docs/source/README.md 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/README.md 2019-03-29 13:47:26.000000000 +0000 @@ -3,10 +3,17 @@ [![Build Status](https://travis-ci.org/torproject/sbws.svg?branch=master)](https://travis-ci.org/https://travis-ci.org/torproject/sbws) Simple Bandwidth Scanner (called `sbws`) is a Tor bandwidth scanner that -produces bandwidth measurements files to be used by Directory Authorities. +generates bandwidth files to be used by Directory Authorities. -The scanner builds two hop circuits consisting of the relay being measured and -a fast exit. Over these circuits it measures bandwidth. +The scanner measures the bandwidth of each relay in the Tor network +(except the directory authorities) by creating a two hops circuit +with the relay. It then measures the bandwidth by downloading data +from a destination Web Server and stores the measurements. + +The generator read the measurements, aggregates, filters and +scales them using torflow's scaling method. +Then it generates a bandwidth list file that is read +by a directory authority to report relays’ bandwidth in its vote. **WARNING**: This software is intended to be run by researchers using a test Tor network, such as chutney or shadow, or by the Tor bandwidth authorities @@ -14,35 +21,38 @@ Please do not run this software on the public Tor network unless you are one of the Tor bandwidth authorities, to avoid creating unnecessary traffic. +**ADVICE**: It is recommended to read this documentation at +[Read the Docs](https://sbws.rtfd.io). In +[Github](https://github.com/torproject/sbws) some links won't be properly +rendered. +It can also be read after installing the Debian package ``sbws-doc`` in +``/usr/share/doc/sbws`` or after building it locally as explained in +``./docs/source/documenting.rst``. + + Installing ------------ -See ./INSTALL.rst (or [INSTALL](./INSTALL.rst) or [INSTALL](./INSTALL.html) ) +See [./INSTALL.rst](INSTALL.rst) (in local directory or GitHub) or +[INSTALL.html](INSTALL.html) (local build or Read the Docs). Deploying and running --------------------- -See ./DEPLOY.rst (or [DEPLOY](./DEPLOY.rst) or [DEPLOY](./DEPLOY.html) ) - -Contributing --------------- - -See ./CONTRIBUTING.rst (or [CONTRIBUTING](./CONTRIBUTING.rst) or -[CONTRIBUTING](./CONTRIBUTING.html) ) +See [./DEPLOY.rst](DEPLOY.rst) (in local directory or GitHub) or +[DEPLOY.html](DEPLOY.html) (local build or Read the Docs). Changelog -------------- -See ./CHANGELOG.rst (or [CHANGELOG](./CHANGELOG.rst) or -[CHANGELOG](./CHANGELOG.html) ) +See [./CHANGELOG.rst](CHANGELOG.rst) (in local directory or GitHub) or +[CHANGELOG.html](CHANGELOG.html) (local build or Read the Docs). Documentation -------------- -More extensive documentation can be found in the ./docs directory, -and online at [sbws.rtfd.io](https://sbws.readthedocs.io) and -[this onion service](http://d7pxflytfsmz6uh3x7i2jxzzwea6nbpmtsz5tmfkcin5edapaig5vpyd.onion/) -([v2](http://sdmb3rfvp3wadu6y.onion/)). +More extensive documentation can be found in the ``./docs`` directory, +and online at [sbws.rtfd.io](https://sbws.readthedocs.io). ## License @@ -56,4 +66,5 @@ ## Authors -See ./AUTHORS.md (or [AUTHORS](./AUTHORS.MD) \ No newline at end of file +See [./AUTHORS.md](AUTHORS.md) (in local directory or GitHub) or +[AUTHORS.html](AUTHORS.html) (local build or Read the Docs). \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/sbws.rst sbws-1.1.0/docs/source/sbws.rst --- sbws-1.0.2/docs/source/sbws.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/sbws.rst 2019-03-29 13:47:26.000000000 +0000 @@ -1,4 +1,4 @@ -sbws package API +Package API ================= Subpackages diff -Nru sbws-1.0.2/docs/source/specification.rst sbws-1.1.0/docs/source/specification.rst --- sbws-1.0.2/docs/source/specification.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/specification.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,207 +0,0 @@ -Simple Bandwidth Scanner technical details -============================================ - -:Author: Matt Traudt, juga -:Date: 29 March 2018 -:Last Update: 19 Sep 2018 -:Status: Draft - -.. note:: this document may become an specification of the ``sbws`` method to - measure relays and/or scale the measuremensts - -Conventions ------------ - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", -"SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" -in this document are to be interpreted as described in BCP 14 (:rfc:`2119` -and :rfc:`8174`) when, and only when, they appear in all capitals, as shown -here. - -Background ----------- - -Some of the Tor :term:`directory authorities ` -run bandwidth scanners to measure the bandwidth of relays and include their -measurements in their network status votes. Clients use the consensus of these -weights to inform their path selection process with the hope that every circuit -they build will have roughly equal performance, regardless of the relays -chosen. This achieves a form of load balancing. - -.. _problem: - -Historically, the directory authorities that ran bandwidth scanners -(:term:`bandwidth authorities `), ran torflow_. Time -passed, it slowly become less maintained, and the collective knowledge of how -it worked slipped away. - -The bandwidth authorities became increasingly unhappy having to run torflow. -Twice yearly Tor Project meetings came and went. Everyone agreed a replacement -was needed, but it was harder to reach consensus on what it should look like -and what its goals where. - -Simple Bandwidth Scanner (sbws) is a project motivated by discussions at the -Rome 2018 Tor Project meeting. It aims to be a quick to implement, -easy to maintain replacement for torflow. It should not receive many fancy -features; instead, developer time should be spent on finding and implementing a -better load balancing solution than bandwidth scanning, such as peerflow_. - -This document describes the implementation contained within the accompanying -``sbws`` package. - -Anatomy of a Tor network using sbws ------------------------------------ - -First and foremost, there needs to be one or more webservers that exist -somewhere on the Internet. They MUST serve up a file of at least some minimum -size, and MUST support both HTTP GET and HEAD requests on that file. For both -HTTP verbs, they MUST support requests with the Range header and MUST support -*not* compressing responses. Beyond these requirements, the webservers MAY support -TLS connections, optionally with a valid certificate. Both apache and nginx fit -these requirements. - -Every directory authority that wishes to also vote on relay bandwidth (AKA -a bandwidth authority) MUST run one or more sbws scanner clients (or trust -someone to run one or more sbws scanner clients for them). The scanners run -continuously, constantly building two-hop circuits to the previously described -webservers and measuring the amount of bandwidth relays are capable of -handling on these measurement circuit. Over these circuits it collects RTT -data (by repeatedly requesting a single byte from the webserver) and available -bandwidth data (by starting small and progressively requesting larger amounts -of data until the request takes long enough to fulfill, and then requesting -that amount many times). - -Periodically the operator of an sbws scanner MUST run the sbws generate -command in order to generate a :term:`v3bw file`. This aggregates the previous -few days' worth of measurement results into one RTT and one bandwidth per relay -ever measured within the validity period into a single file for the tor process -the bandwidth authority is running to read. The bandwidth authority includes -these aggregated results in its votes. - -Sbws dependencies ------------------ - -Sbws scanners run tor for themselves and do not require a system tor process to -exist. Tor MUST be installed, and it SHOULD be any up-to-date version supported -by the network team. - -Sbws uses the python library Stem_ to launch and control tor and the python -library Requests_ to make HTTP(S) requests. Both are generally packaged for -most major Linux distributions, and are always available in PyPI. - -How it all works ----------------- - -We now describe various core parts of sbws. - -Simple relay prioritization -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This may be the most complex part of sbws. - -Sbws makes an effort to prioritize measurements of relays that don't have many -recent results. For example: relays that just joined the Tor network or relays -that haven't been online in the past few days. This goal is achieved using a -min-priority queue and the concept of *freshness*. - -Freshness is defined as the amount of time between when the measurement was -made and the time sbws will stop considering it valid. Thus, a measurement made -more recently will have more time until it is no longer valid (higher -freshness) and a measurement made a long time ago will have very little time -until it is no longer valid (lower freshness). - -Over time, sbws will make many measurements for a given relay. The sum of these -measurements' freshnesses is the relay's priority. As we are prioritizing like -a min-priority queue, a higher sum of freshnesses means *worse* priority while -a lower sum of freshnesses means *better* priority. - - Example: AlphaRelay33 joined the network yesterday and sbws has measured it - once so far. BetaRelay87 has been in the network for years and has been - getting measured regularly approximately once a day. BetaRelay87 has five - measurements that are still valid, with freshnesses 10, 100, 500, 1000, and - 1500. AlphaRelay33's one measurement has freshness 3000. Because the sum of - BetaRelay87's 5 measurements is greater than AlphaRelay33's one measurement, - AlphaRelay33 has *better* priority and will be measured next before - BetaRelay87. - - Example: AlphaRelay33 is still a brand new relay with its one measurement - in the last day with freshness 3000. CharlieRelay9 has been in the network - for a long time, but had technical issues last week and hasn't been online in - many days. When CharlieRelay9's operator finally gets him back online, he - still has one valid measurement with freshness 10. Because AlphaRelay33's - measurement is fresher, CharlieRelay9 has *better* priority and will get - measured first. - -Sometimes measurements fail. Hopefully they fail because of transient issues, -and with that hope in mind, it would be nice if a relay with a failed -measurement didn't have to wait a long time to have another chance at a -successful measurement. For this reason, when summing the freshnesses of -results for a given relay, sbws will artificially *reduce* the freshness for -measurements that were not successful. This makes the sum of freshnesses lower -for that relay, and therefore the priority *better* so it can be measured again -sooner. - -Simple result storage -~~~~~~~~~~~~~~~~~~~~~ - -Internally, sbws has a hierarchy of ``Result`` classes for easy managing of -different types of result (success, error-because-of-circuit-error, -error-because-[...] etc.). These results get converted into JSON strings and -stored -- **one per line** -- in text files in a data directory. - -The text files are simply named after the date. For example: -``2018-03-20.txt``. - -The sbws scanner only appends to these files, and it automatically starts a new -file when the system's clock ticks past midnight UTC. - -To avoid any weird timezone-related issues, consumers of sbws scanner data (such -as the generate and stats scripts) SHOULD read more files than strictly -necessary. For example, if the validity period is 5 days, they should read 6 -days of files. Because all results have a Unix timestamp, consumers of sbws -data can easily determine which results are just outside the validity period as -they are reading them in. - - -Simple result processing -~~~~~~~~~~~~~~~~~~~~~~~~ - -Every hour the directory authorities vote to come to a consensus about the -state of the Tor network. The bandwidth authorities need to use the results -that have been gathered to inform their vote about relays' bandwidths. To do -this they use sbws generate. - -The relays' bandwidth measurements (``Results``) to be added to the Bandwidth -File MUST be first selected and MUST be then then scaled. - -Selecting bandwidth measurements -::::::::::::::::::::::::::::::::::: - -Each relay bandwidth measurements are selected in the following way: - -1. At least two bandwidth measurements (``Result`` s) MUST have been obtained - within an arbitrary number of seconds (currently one day). - If they are not, the relay MUST NOT be included in the Bandwith File. -2. The measurements than are are older than an arbitrary number of senconds - in the past MUST be discarded. - Currently this number is the same as ``data_period`` (5 days). - -If the number of relays to include in the Bandwidth File are less than -a percententage (currently 60%) than the number of relays in the consensus, -additional Header Lines MUST be added (see XXX) to the Bandwith File and the -relays SHOULD NOT be included. - -Scaling bandwidth measurements -::::::::::::::::::::::::::::::::: - -Consensus bandwidth obtained by new implementations MUST be comparable to the -consensus bandwidth, therefore they MUST implement torflow_scaling_. -The bandwidth_file_spec_ appendix B describes torflow scaling and a linear -scaling method. - -.. _torflow: https://gitweb.torproject.org/torflow.git -.. _stem: https://stem.torproject.org -.. _requests: https://docs.python-requests.org/ -.. _peerflow: https://www.nrl.navy.mil/itd/chacs/sites/www.nrl.navy.mil.itd.chacs/files/pdfs/16-1231-4353.pdf -.. _torflow_scaling: https://gitweb.torproject.org/torflow.git/tree/NetworkScanners/BwAuthority/README.spec.txt#n298 -.. _bandwidth_file_spec: https://gitweb.torproject.org/torspec.git/tree/bandwidth-file-spec.txt \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/state.rst sbws-1.1.0/docs/source/state.rst --- sbws-1.0.2/docs/source/state.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/state.rst 2019-03-29 13:47:26.000000000 +0000 @@ -18,3 +18,5 @@ - **Producer**: ``sbws scanner``, once at startup. - **Consumer**: ``sbws generate``, once each time it is ran. + +Code: :class:`sbws.util.state.State` \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/testing.rst sbws-1.1.0/docs/source/testing.rst --- sbws-1.0.2/docs/source/testing.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/docs/source/testing.rst 2019-03-29 13:47:26.000000000 +0000 @@ -18,7 +18,7 @@ tox -.. _Flake8: http://flake8.pycqa.org/ +.. _Flake8: https://flake8.readthedocs.io/ .. _pytest: https://docs.pytest.org/ .. _tox: https://tox.readthedocs.io .. _Coverage: https://coverage.readthedocs.io/ \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/threads.puml sbws-1.1.0/docs/source/threads.puml --- sbws-1.0.2/docs/source/threads.puml 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/threads.puml 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,23 @@ +@startuml + +start + +:MainThread; + fork + :TorEventListener; + fork again + :ResultDump; + fork again + :workers_thread; + fork again + :tasks_thread; + fork again + :resutls_tread; + fork again + while (threads < 3?) + :measure_relay; + endwhile + end fork + + +@enduml \ No newline at end of file diff -Nru sbws-1.0.2/docs/source/tor_bandwidth_files.rst sbws-1.1.0/docs/source/tor_bandwidth_files.rst --- sbws-1.0.2/docs/source/tor_bandwidth_files.rst 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/docs/source/tor_bandwidth_files.rst 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,32 @@ +How bandwidth files are shown in the Tor network +================================================= + +Directory authorities' votes +----------------------------- + +moria, using Tor 0.3.5.7: + +.. code:: text + + bandwidth-file-headers timestamp=1548181637 + +https://collector.torproject.org/recent/relay-descriptors/votes/ + +To appear in Tor v0.4.1.x: + +.. code:: text + + bandwidth-file-digest sha256=01234567890123456789abcdefghijkl + +https://trac.torproject.org/projects/tor/ticket/26698 + +Directory authorities' bandwidth file URL +----------------------------------------- + +To appear in Tor v0.4.1.x: + +.. code:: text + + /tor/status-vote/next/bandwidth.z + +https://trac.torproject.org/projects/tor/ticket/21377 diff -Nru sbws-1.0.2/.gitignore sbws-1.1.0/.gitignore --- sbws-1.0.2/.gitignore 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/.gitignore 2019-03-29 13:47:26.000000000 +0000 @@ -1,3 +1,4 @@ +*.pyc __pycache__/ venv*/ passwords.txt @@ -9,3 +10,5 @@ htmlcov .pytest_cache dist +build +*.lockfile diff -Nru sbws-1.0.2/INSTALL.rst sbws-1.1.0/INSTALL.rst --- sbws-1.0.2/INSTALL.rst 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/INSTALL.rst 2019-03-29 13:47:26.000000000 +0000 @@ -19,7 +19,7 @@ System requirements -------------------- -- Tor +- Tor (last stable version is recommended) - Python 3 (>= 3.5) - virtualenv_ (while there is not ``stem`` release > 1.6.0, it is recommended to install the required python dependencies in a virtualenv) @@ -43,7 +43,7 @@ Clone ``sbws``:: - git clone https://gitweb.torproject.org/sbws.git + git clone https://git.torproject.org/sbws.git Install the python dependencies:: @@ -51,13 +51,14 @@ ``sbws`` needs :term:`destination` s to request files from. -Please, see ./DEPLOY.rst (or `/DEPLOY.rst `_ or :ref:`deploy`) +Please, see ``_ (in the local directory or GitHub) or +``_ (local build or Read the Docs) to configure, deploy and run ``sbws``. System physical requirements ----------------------------- -- Bandwidth: at least 20MB/s (160 Mbit/s). The more the better. +- Bandwidth: at least 12.5MB/s (100 Mbit/s). - Free RAM: at least 1.5GB - Free disk: at least 3GB @@ -82,8 +83,11 @@ .. _virtualenv: https://virtualenv.pypa.io/en/stable/installation/ .. _Stem: https://stem.torproject.org/ .. _socks: http://docs.python-requests.org/en/master/user/advanced/#socks +.. https://readthedocs.org/projects/requests/ redirect to this, but the +.. certificate of this signed by rtd .. _Requests: http://docs.python-requests.org/ -.. _Flake8: http://flake8.pycqa.org/ +.. http://flake8.pycqa.org/ certificate is signed by rtf +.. _Flake8: https://flake8.readthedocs.org/ .. _pytest: https://docs.pytest.org/ .. _tox: https://tox.readthedocs.io .. _Coverage: https://coverage.readthedocs.io/ diff -Nru sbws-1.0.2/MANIFEST.in sbws-1.1.0/MANIFEST.in --- sbws-1.0.2/MANIFEST.in 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/MANIFEST.in 2019-03-29 13:47:26.000000000 +0000 @@ -1,14 +1,7 @@ include *.md include *.rst -include sbws/*.ini -include examples * +include *.ini recursive-include docs * prune docs/build recursive-include tests * -prune tests/testnets/*/auth? -prune tests/testnets/*/relay? -prune tests/testnets/*/datadir -prune tests/testnets/*/exit? -recursive-exclude tests/testnets/* config*.ini -recursive-exclude tests/testnets/* *.log -prune **/__pycache__ +recursive-exclude **/__pycache__ * diff -Nru sbws-1.0.2/README.md sbws-1.1.0/README.md --- sbws-1.0.2/README.md 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/README.md 2019-03-29 13:47:26.000000000 +0000 @@ -3,10 +3,17 @@ [![Build Status](https://travis-ci.org/torproject/sbws.svg?branch=master)](https://travis-ci.org/https://travis-ci.org/torproject/sbws) Simple Bandwidth Scanner (called `sbws`) is a Tor bandwidth scanner that -produces bandwidth measurements files to be used by Directory Authorities. +generates bandwidth files to be used by Directory Authorities. -The scanner builds two hop circuits consisting of the relay being measured and -a fast exit. Over these circuits it measures bandwidth. +The scanner measures the bandwidth of each relay in the Tor network +(except the directory authorities) by creating a two hops circuit +with the relay. It then measures the bandwidth by downloading data +from a destination Web Server and stores the measurements. + +The generator read the measurements, aggregates, filters and +scales them using torflow's scaling method. +Then it generates a bandwidth list file that is read +by a directory authority to report relays’ bandwidth in its vote. **WARNING**: This software is intended to be run by researchers using a test Tor network, such as chutney or shadow, or by the Tor bandwidth authorities @@ -14,35 +21,38 @@ Please do not run this software on the public Tor network unless you are one of the Tor bandwidth authorities, to avoid creating unnecessary traffic. +**ADVICE**: It is recommended to read this documentation at +[Read the Docs](https://sbws.rtfd.io). In +[Github](https://github.com/torproject/sbws) some links won't be properly +rendered. +It can also be read after installing the Debian package ``sbws-doc`` in +``/usr/share/doc/sbws`` or after building it locally as explained in +``./docs/source/documenting.rst``. + + Installing ------------ -See ./INSTALL.rst (or [INSTALL](./INSTALL.rst) or [INSTALL](./INSTALL.html) ) +See [./INSTALL.rst](INSTALL.rst) (in local directory or GitHub) or +[INSTALL.html](INSTALL.html) (local build or Read the Docs). Deploying and running --------------------- -See ./DEPLOY.rst (or [DEPLOY](./DEPLOY.rst) or [DEPLOY](./DEPLOY.html) ) - -Contributing --------------- - -See ./CONTRIBUTING.rst (or [CONTRIBUTING](./CONTRIBUTING.rst) or -[CONTRIBUTING](./CONTRIBUTING.html) ) +See [./DEPLOY.rst](DEPLOY.rst) (in local directory or GitHub) or +[DEPLOY.html](DEPLOY.html) (local build or Read the Docs). Changelog -------------- -See ./CHANGELOG.rst (or [CHANGELOG](./CHANGELOG.rst) or -[CHANGELOG](./CHANGELOG.html) ) +See [./CHANGELOG.rst](CHANGELOG.rst) (in local directory or GitHub) or +[CHANGELOG.html](CHANGELOG.html) (local build or Read the Docs). Documentation -------------- -More extensive documentation can be found in the ./docs directory, -and online at [sbws.rtfd.io](https://sbws.readthedocs.io) and -[this onion service](http://d7pxflytfsmz6uh3x7i2jxzzwea6nbpmtsz5tmfkcin5edapaig5vpyd.onion/) -([v2](http://sdmb3rfvp3wadu6y.onion/)). +More extensive documentation can be found in the ``./docs`` directory, +and online at [sbws.rtfd.io](https://sbws.readthedocs.io). ## License @@ -56,4 +66,5 @@ ## Authors -See ./AUTHORS.md (or [AUTHORS](./AUTHORS.MD) \ No newline at end of file +See [./AUTHORS.md](AUTHORS.md) (in local directory or GitHub) or +[AUTHORS.html](AUTHORS.html) (local build or Read the Docs). \ No newline at end of file diff -Nru sbws-1.0.2/.readthedocs.yml sbws-1.1.0/.readthedocs.yml --- sbws-1.0.2/.readthedocs.yml 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/.readthedocs.yml 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,13 @@ +# .readthedocs.yml + +build: + image: latest + +python: + version: 3.5 + # To run "pip install ." in rtfd.io + pip_install: true + +# To run "pip install .[docs]" in rtfd.io +extra_requirements: + - docs diff -Nru sbws-1.0.2/sbws/config.default.ini sbws-1.1.0/sbws/config.default.ini --- sbws-1.0.2/sbws/config.default.ini 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/config.default.ini 2019-03-29 13:47:26.000000000 +0000 @@ -36,6 +36,9 @@ [scanner] # A human-readable string with chars in a-zA-Z0-9 to identify your scanner nickname = IDidntEditTheSBWSConfig +# ISO 3166-1 alpha-2 country code. To be edited. +# Default to a non existing country to detect it was not edited. +country = AA # Limits on what download times are too fast/slow/etc. download_toofast = 1 download_min = 5 @@ -94,10 +97,12 @@ [logging] # Whether or not to log to a rotating file the directory paths.log_dname -to_file = no +to_file = yes # Whether or not to log to stdout to_stdout = yes # Whether or not to log to syslog +# NOTE that when sbws is launched by systemd, stdout goes to journal and +# syslog. to_syslog = no # If logging to file, how large (in bytes) should the file be allowed to get # before rotating to a new one. 10485760 is 10 MiB. If zero or number of @@ -107,14 +112,15 @@ # never rotate the log file. to_file_num_backups = 50 # Level to log at. Debug, info, warning, error, critical. -level = info -to_file_level = ${level} -to_stdout_level = ${level} -to_syslog_level = ${level} +# `level` must be set to the lower of all the handler levels. +level = debug +to_file_level = debug +to_stdout_level = info +to_syslog_level = info # Format string to use when logging -format = [%(asctime)s] [%(name)s] [%(levelname)s] %(message)s -to_file_format = ${format} +format = %(asctime)s %(module)s[%(process)s]: <%(levelname)s> %(message)s to_stdout_format = ${format} to_syslog_format = %(module)s[%(process)s]: <%(levelname)s> %(message)s # verbose formatter useful for debugging -#format = %(asctime)s %(levelname)s %(threadName)s %(filename)s:%(lineno)s - %(funcName)s - %(message)s +to_file_format = %(asctime)s %(levelname)s %(threadName)s %(filename)s:%(lineno)s - %(funcName)s - %(message)s + diff -Nru sbws-1.0.2/sbws/config.log.default.ini sbws-1.1.0/sbws/config.log.default.ini --- sbws-1.0.2/sbws/config.log.default.ini 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/config.log.default.ini 2019-03-29 13:47:26.000000000 +0000 @@ -34,7 +34,10 @@ args = ('/dev/log',) [formatter_to_stdout] +# format date as syslog and journal +datefmt = %b %d %H:%M:%S [formatter_to_file] +datefmt = %b %d %H:%M:%S [formatter_to_syslog] diff -Nru sbws-1.0.2/sbws/core/cleanup.py sbws-1.1.0/sbws/core/cleanup.py --- sbws-1.0.2/sbws/core/cleanup.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/core/cleanup.py 2019-03-29 13:47:26.000000000 +0000 @@ -122,7 +122,7 @@ def _clean_v3bw_files(args, conf): - v3bw_dname = conf['paths']['v3bw_dname'] + v3bw_dname = conf.getpath('paths', 'v3bw_dname') if not os.path.isdir(v3bw_dname): fail_hard('%s does not exist', v3bw_dname) compress_after_days = conf.getint('cleanup', @@ -144,7 +144,7 @@ def _clean_result_files(args, conf): - datadir = conf['paths']['datadir'] + datadir = conf.getpath('paths', 'datadir') if not os.path.isdir(datadir): fail_hard('%s does not exist', datadir) data_period = conf.getint('general', 'data_period') @@ -174,10 +174,6 @@ :param argparse.Namespace args: command line arguments :param configparser.ConfigParser conf: parsed config files ''' - datadir = conf.getpath('paths', 'datadir') - if not os.path.isdir(datadir): - fail_hard('%s does not exist', datadir) - if not args.no_results: _clean_result_files(args, conf) diff -Nru sbws-1.0.2/sbws/core/generate.py sbws-1.1.0/sbws/core/generate.py --- sbws-1.0.2/sbws/core/generate.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/core/generate.py 2019-03-29 13:47:26.000000000 +0000 @@ -1,7 +1,7 @@ from math import ceil from sbws.globals import (fail_hard, SBWS_SCALE_CONSTANT, TORFLOW_SCALING, - SBWS_SCALING, TORFLOW_BW_MARGIN, TORFLOW_ROUND_DIG, + SBWS_SCALING, TORFLOW_BW_MARGIN, PROP276_ROUND_DIG, DAY_SECS, NUM_MIN_RESULTS) from sbws.lib.v3bwfile import V3BWFile from sbws.lib.resultdump import load_recent_results_in_datadir @@ -9,6 +9,7 @@ import os import logging from sbws.util.timestamp import now_fname +from sbws.lib import destination log = logging.getLogger(__name__) @@ -22,7 +23,9 @@ '(latest.v3bw is an atomically created symlink in the same '\ 'directory as output.) '\ 'If the file is transferred to another host, it should be written to '\ - 'a temporary path, then renamed to the V3BandwidthsFile path.' + 'a temporary path, then renamed to the V3BandwidthsFile path.\n'\ + 'The default scaling method is torflow\'s one. To use different'\ + 'scaling methods or no scaling, see the options.' p = sub.add_parser('generate', description=d, formatter_class=ArgumentDefaultsHelpFormatter) p.add_argument('--output', default=None, type=str, @@ -40,20 +43,21 @@ 'are, but scale them such that we have a budget of ' 'scale_constant * num_measured_relays = bandwidth to give ' 'out, and we do so proportionally') - p.add_argument('-t', '--scale-torflow', action='store_const', - default=True, const=False, - help='If specified, do not use bandwidth values as they ' - 'are, but scale them in the way Torflow does.') + p.add_argument('-t', '--scale-torflow', action='store_true', + default=True, + help='If specified, scale measurements using torflow\'s ' + 'method. This option is kept for compatibility with older ' + 'versions and it is silently ignored, since it is the ' + 'default.') p.add_argument('-w', '--raw', action='store_true', help='If specified, do use bandwidth raw measurements ' 'without any scaling.') p.add_argument('-m', '--torflow-bw-margin', default=TORFLOW_BW_MARGIN, type=float, help="Cap maximum bw when scaling as Torflow. ") - p.add_argument('-r', '--torflow-round-digs', default=TORFLOW_ROUND_DIG, - type=int, - help="Number of most significant digits to round bw " - "when scaling as Torflow.") + p.add_argument('-r', '--round-digs', '--torflow-round-digs', + default=PROP276_ROUND_DIG, type=int, + help="Number of most significant digits to round bw.") p.add_argument('-p', '--secs-recent', default=None, type=int, help="How many secs in the past are results being " "still considered. Note this value will supersede " @@ -63,6 +67,7 @@ "other.") p.add_argument('-n', '--min-num', default=NUM_MIN_RESULTS, type=int, help="Mininum number of a results to consider them.") + return p def main(args, conf): @@ -80,6 +85,8 @@ elif args.raw: scaling_method = None else: + # sbws will scale as torflow until we have a better algorithm for + # scaling (#XXX) scaling_method = TORFLOW_SCALING if args.secs_recent: fresh_days = ceil(args.secs_recent / 24 / 60 / 60) @@ -88,7 +95,7 @@ reset_bw_ipv4_changes = conf.getboolean('general', 'reset_bw_ipv4_changes') reset_bw_ipv6_changes = conf.getboolean('general', 'reset_bw_ipv6_changes') results = load_recent_results_in_datadir( - fresh_days, datadir, success_only=True, + fresh_days, datadir, on_changed_ipv4=reset_bw_ipv4_changes, on_changed_ipv6=reset_bw_ipv6_changes) if len(results) < 1: @@ -98,10 +105,14 @@ state_fpath = conf.getpath('paths', 'state_fname') consensus_path = os.path.join(conf.getpath('tor', 'datadir'), "cached-consensus") - bw_file = V3BWFile.from_results(results, state_fpath, args.scale_constant, - scaling_method, + # Accept None as scanner_country to be compatible with older versions. + scanner_country = conf['scanner'].get('country') + destinations_countries = destination.parse_destinations_countries(conf) + bw_file = V3BWFile.from_results(results, scanner_country, + destinations_countries, state_fpath, + args.scale_constant, scaling_method, torflow_cap=args.torflow_bw_margin, - torflow_round_digs=args.torflow_round_digs, + round_digs=args.round_digs, secs_recent=args.secs_recent, secs_away=args.secs_away, min_num=args.min_num, diff -Nru sbws-1.0.2/sbws/core/scanner.py sbws-1.1.0/sbws/core/scanner.py --- sbws-1.0.2/sbws/core/scanner.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/core/scanner.py 2019-03-29 13:47:26.000000000 +0000 @@ -1,47 +1,108 @@ ''' Measure the relays. ''' +import queue + +import signal +import sys +import threading +import traceback +import uuid + +from multiprocessing.context import TimeoutError from ..lib.circuitbuilder import GapsCircuitBuilder as CB from ..lib.resultdump import ResultDump -from ..lib.resultdump import ResultSuccess, ResultErrorCircuit -from ..lib.resultdump import ResultErrorStream +from ..lib.resultdump import ( + ResultSuccess, ResultErrorCircuit, ResultErrorStream, + ResultErrorSecondRelay, ResultError, ResultErrorDestination + ) from ..lib.relaylist import RelayList from ..lib.relayprioritizer import RelayPrioritizer -from ..lib.destination import DestinationList +from ..lib.destination import (DestinationList, + connect_to_destination_over_circuit) from ..util.timestamp import now_isodt_str from ..util.state import State -from sbws.globals import fail_hard +from sbws.globals import fail_hard, HTTP_GET_HEADERS, TIMEOUT_MEASUREMENTS import sbws.util.stem as stem_utils import sbws.util.requests as requests_utils from argparse import ArgumentDefaultsHelpFormatter from multiprocessing.dummy import Pool -from threading import Event import time import os import logging -import requests import random +from .. import settings +from ..lib.heartbeat import Heartbeat rng = random.SystemRandom() -end_event = Event() log = logging.getLogger(__name__) +# Declare the objects that manage the threads global so that sbws can exit +# gracefully at any time. +pool = None +rd = None +controller = None + +FILLUP_TICKET_MSG = """Something went wrong. +Please create a ticket in https://trac.torproject.org with this traceback.""" + + +def stop_threads(signal, frame, exit_code=0): + global rd, pool + log.debug('Stopping sbws.') + # Avoid new threads to start. + settings.set_end_event() + # Stop Pool threads + pool.close() + pool.join() + # Stop ResultDump thread + rd.thread.join() + # Stop Tor thread + controller.close() + sys.exit(exit_code) + + +signal.signal(signal.SIGTERM, stop_threads) + + +def dumpstacks(): + log.critical(FILLUP_TICKET_MSG) + thread_id2name = dict([(t.ident, t.name) for t in threading.enumerate()]) + for thread_id, stack in sys._current_frames().items(): + log.critical("Thread: %s(%d)", + thread_id2name.get(thread_id, ""), thread_id) + log.critical(traceback.format_stack("".join(stack))) + # If logging level is less than DEBUG (more verbose), start pdb so that + # developers can debug the issue. + if log.getEffectiveLevel() < logging.DEBUG: + import pdb + pdb.set_trace() + # Otherwise exit. + else: + # Change to stop threads when #28869 is merged + sys.exit(1) def timed_recv_from_server(session, dest, byte_range): ''' Request the **byte_range** from the URL at **dest**. If successful, return True and the time it took to download. Otherwise return False and an exception. ''' - headers = {'Range': byte_range, 'Accept-Encoding': 'identity'} + start_time = time.time() - # TODO: - # - What other exceptions can this throw? - # - Do we have to read the content, or did requests already do so? + HTTP_GET_HEADERS['Range'] = byte_range + # - response.elapsed "measures the time taken between sending the first + # byte of the request and finishing parsing the headers. + # It is therefore unaffected by consuming the response content" + # If this mean that the content has arrived, elapsed could be used to + # know the time it took. try: - requests_utils.get( - session, dest.url, headers=headers, verify=dest.verify) - except requests.exceptions.ConnectionError as e: - return False, e - except requests.exceptions.ReadTimeout as e: + # headers are merged with the session ones, not overwritten. + session.get(dest.url, headers=HTTP_GET_HEADERS, verify=dest.verify) + # All `requests` exceptions could be caught with + # `requests.exceptions.RequestException`, but it seems that `requests` + # does not catch all the ssl exceptions and urllib3 doesn't seem to have + # a base exception class. + except Exception as e: + log.debug(e) return False, e end_time = time.time() return True, end_time - start_time @@ -75,27 +136,37 @@ ''' Make multiple end-to-end RTT measurements by making small HTTP requests over a circuit + stream that should already exist, persist, and not need rebuilding. If something goes wrong and not all of the RTT measurements can - be made, return None. Otherwise return a list of the RTTs (in seconds). ''' + be made, return None. Otherwise return a list of the RTTs (in seconds). + + :returns tuple: results or None if the if the measurement fail. + None or exception if the measurement fail. + + ''' rtts = [] size = conf.getint('scanner', 'min_download_size') - log.debug('Measuring RTT to %s', dest.url) for _ in range(0, conf.getint('scanner', 'num_rtts')): + log.debug('Measuring RTT to %s', dest.url) random_range = get_random_range_string(content_length, size) success, data = timed_recv_from_server(session, dest, random_range) if not success: # data is an exception - log.warning('While measuring the RTT to %s we hit an exception ' - '(does the webserver support Range requests?): %s', - dest.url, data) - return None + log.debug('While measuring the RTT to %s we hit an exception ' + '(does the webserver support Range requests?): %s', + dest.url, data) + return None, data assert success # data is an RTT assert isinstance(data, float) or isinstance(data, int) rtts.append(data) - return rtts + return rtts, None def measure_bandwidth_to_server(session, conf, dest, content_length): + """ + :returns tuple: results or None if the if the measurement fail. + None or exception if the measurement fail. + + """ results = [] num_downloads = conf.getint('scanner', 'num_downloads') expected_amount = conf.getint('scanner', 'initial_read_request') @@ -107,17 +178,17 @@ 'target': conf.getfloat('scanner', 'download_target'), 'max': conf.getfloat('scanner', 'download_max'), } - while len(results) < num_downloads: + while len(results) < num_downloads and not settings.end_event.is_set(): assert expected_amount >= min_dl assert expected_amount <= max_dl random_range = get_random_range_string(content_length, expected_amount) success, data = timed_recv_from_server(session, dest, random_range) if not success: # data is an exception - log.warning('While measuring the bandwidth to %s we hit an ' - 'exception (does the webserver support Range ' - 'requests?): %s', dest.url, data) - return None + log.debug('While measuring the bandwidth to %s we hit an ' + 'exception (does the webserver support Range ' + 'requests?): %s', dest.url, data) + return None, data assert success # data is a download time assert isinstance(data, float) or isinstance(data, int) @@ -127,7 +198,7 @@ 'duration': data, 'amount': expected_amount}) expected_amount = _next_expected_amount( expected_amount, data, download_times, min_dl, max_dl) - return results + return results, None def _pick_ideal_second_hop(relay, dest, rl, cont, is_exit): @@ -136,14 +207,14 @@ destination **dest**, pick a second relay that is or is not an exit according to **is_exit**. ''' - candidates = [] - candidates.extend(rl.exits if is_exit else rl.non_exits) + candidates = rl.exits_not_bad_allowing_port(dest.port) if is_exit \ + else rl.non_exits if not len(candidates): return None log.debug('Picking a 2nd hop to measure %s from %d choices. is_exit=%s', relay.nickname, len(candidates), is_exit) for min_bw_factor in [2, 1.75, 1.5, 1.25, 1]: - min_bw = relay.bandwidth * min_bw_factor + min_bw = relay.consensus_bandwidth * min_bw_factor new_candidates = stem_utils.only_relays_with_bandwidth( cont, candidates, min_bw=min_bw) if len(new_candidates) > 0: @@ -152,112 +223,163 @@ 'Found %d candidate 2nd hops with at least %sx the bandwidth ' 'of %s. Returning %s (bw=%s).', len(new_candidates), min_bw_factor, relay.nickname, - chosen.nickname, chosen.bandwidth) + chosen.nickname, chosen.consensus_bandwidth) return chosen - candidates = sorted(candidates, key=lambda r: r.bandwidth, reverse=True) + candidates = sorted(candidates, key=lambda r: r.consensus_bandwidth, + reverse=True) chosen = candidates[0] log.debug( 'Didn\'t find any 2nd hops at least as fast as %s (bw=%s). It\'s ' 'probably really fast. Returning %s (bw=%s), the fastest ' - 'candidate we have.', relay.nickname, relay.bandwidth, - chosen.nickname, chosen.bandwidth) + 'candidate we have.', relay.nickname, relay.consensus_bandwidth, + chosen.nickname, chosen.consensus_bandwidth) return chosen def measure_relay(args, conf, destinations, cb, rl, relay): + """ + Select a Web server, a relay to build the circuit, + build the circuit and measure the bandwidth of the given relay. + + :return Result: a measurement Result object + + """ + log.debug('Measuring %s %s', relay.nickname, relay.fingerprint) + our_nick = conf['scanner']['nickname'] s = requests_utils.make_session( cb.controller, conf.getfloat('general', 'http_timeout')) + # Probably because the scanner is stopping. + if s is None: + if settings.end_event.is_set(): + return None + else: + # In future refactor this should be returned from the make_session + reason = "Unable to get proxies." + log.debug(reason + ' to measure %s %s', + relay.nickname, relay.fingerprint) + return [ + ResultError(relay, [], '', our_nick, + msg=reason), + ] # Pick a destionation dest = destinations.next() + # When there're no any functional destinations. if not dest: - log.warning('Unable to get destination to measure %s %s', - relay.nickname, relay.fingerprint[0:8]) - return None + # NOTE: When there're still functional destinations but only one of + # them fail, the error will be included in `ResultErrorStream`. + # Since this is being executed in a thread, the scanner can not + # be stop here, but the `end_event` signal can be set so that the + # main thread stop the scanner. + # It might be useful to store the fact that the destinations fail, + # so store here the error, and set the signal once the error is stored + # (in `resultump`). + log.critical("There are not any functional destinations.\n" + "It is recommended to set several destinations so that " + "the scanner can continue if one fails.") + reason = "No functional destinations" + # Resultdump will set end_event after storing the error + return [ + ResultErrorDestination(relay, [], '', our_nick, msg=reason), + ] + # Pick a relay to help us measure the given relay. If the given relay is an # exit, then pick a non-exit. Otherwise pick an exit. helper = None circ_fps = None - if relay.can_exit_to(dest.hostname, dest.port) and \ - relay not in rl.bad_exits: + if relay.is_exit_not_bad_allowing_port(dest.port): helper = _pick_ideal_second_hop( relay, dest, rl, cb.controller, is_exit=False) if helper: circ_fps = [helper.fingerprint, relay.fingerprint] + # stored for debugging + nicknames = [helper.nickname, relay.nickname] else: helper = _pick_ideal_second_hop( relay, dest, rl, cb.controller, is_exit=True) if helper: circ_fps = [relay.fingerprint, helper.fingerprint] + nicknames = [relay.nickname, helper.nickname] if not helper: - # TODO: Return ResultError of some sort - log.warning('Unable to pick a 2nd hop to help measure %s %s', - relay.nickname, relay.fingerprint[0:8]) - return None - assert helper - assert circ_fps is not None and len(circ_fps) == 2 + reason = 'Unable to select a second relay' + log.debug(reason + ' to help measure %s (%s)', + relay.fingerprint, relay.nickname) + return [ + ResultErrorSecondRelay(relay, [], dest.url, our_nick, + msg=reason), + ] + # Build the circuit - our_nick = conf['scanner']['nickname'] - circ_id = cb.build_circuit(circ_fps) + circ_id, reason = cb.build_circuit(circ_fps) if not circ_id: - log.warning('Could not build circuit involving %s', relay.nickname) - msg = 'Unable to complete circuit' + log.debug('Could not build circuit with path %s (%s): %s ', + circ_fps, nicknames, reason) return [ - ResultErrorCircuit(relay, circ_fps, dest.url, our_nick, msg=msg), + ResultErrorCircuit(relay, circ_fps, dest.url, our_nick, + msg=reason), ] - log.debug('Built circ %s %s for relay %s %s', circ_id, - stem_utils.circuit_str(cb.controller, circ_id), relay.nickname, - relay.fingerprint[0:8]) - # Make a connection to the destionation webserver and make sure it can - # still help us measure - is_usable, usable_data = dest.is_usable(circ_id, s, cb.controller) + log.debug('Built circuit with path %s (%s) to measure %s (%s)', + circ_fps, nicknames, relay.fingerprint, relay.nickname) + # Make a connection to the destination + is_usable, usable_data = connect_to_destination_over_circuit( + dest, circ_id, s, cb.controller, dest._max_dl) if not is_usable: - log.warning('When measuring %s %s the destination seemed to have ' - 'stopped being usable: %s', relay.nickname, - relay.fingerprint[0:8], usable_data) + log.debug('Destination %s unusable via circuit %s (%s), %s', + dest.url, circ_fps, nicknames, usable_data) cb.close_circuit(circ_id) - # TODO: Return a different/new type of ResultError? - msg = 'The destination seemed to have stopped being usable' return [ - ResultErrorStream(relay, circ_fps, dest.url, our_nick, msg=msg), + ResultErrorStream(relay, circ_fps, dest.url, our_nick, + msg=usable_data), ] assert is_usable assert 'content_length' in usable_data # FIRST: measure RTT - rtts = measure_rtt_to_server(s, conf, dest, usable_data['content_length']) + rtts, reason = measure_rtt_to_server(s, conf, dest, + usable_data['content_length']) if rtts is None: - log.warning('Unable to measure RTT to %s via relay %s %s', - dest.url, relay.nickname, relay.fingerprint[0:8]) + log.debug('Unable to measure RTT for %s (%s) to %s via circuit ' + '%s (%s): %s', relay.fingerprint, relay.nickname, + dest.url, circ_fps, nicknames, reason) cb.close_circuit(circ_id) - # TODO: Return a different/new type of ResultError? - msg = 'Something bad happened while measuring RTTs' return [ - ResultErrorStream(relay, circ_fps, dest.url, our_nick, msg=msg), + ResultErrorStream(relay, circ_fps, dest.url, our_nick, + msg=str(reason)), ] # SECOND: measure bandwidth - bw_results = measure_bandwidth_to_server( + bw_results, reason = measure_bandwidth_to_server( s, conf, dest, usable_data['content_length']) if bw_results is None: - log.warning('Unable to measure bandwidth to %s via relay %s %s', - dest.url, relay.nickname, relay.fingerprint[0:8]) + log.debug('Unable to measure bandwidth for %s (%s) to %s via circuit ' + '%s (%s): %s', relay.fingerprint, relay.nickname, + dest.url, circ_fps, nicknames, reason) cb.close_circuit(circ_id) - # TODO: Return a different/new type of ResultError? - msg = 'Something bad happened while measuring bandwidth' return [ - ResultErrorStream(relay, circ_fps, dest.url, our_nick, msg=msg), + ResultErrorStream(relay, circ_fps, dest.url, our_nick, + msg=str(reason)), ] cb.close_circuit(circ_id) # Finally: store result + log.debug('Success measurement for %s (%s) via circuit %s (%s) to %s', + relay.fingerprint, relay.nickname, circ_fps, nicknames, dest.url) return [ ResultSuccess(rtts, bw_results, relay, circ_fps, dest.url, our_nick), ] def dispatch_worker_thread(*a, **kw): + # If at the point where the relay is actually going to be measured there + # are not any functional destinations or the `end_event` is set, do not + # try to start measuring the relay, since it will fail anyway. try: - return measure_relay(*a, **kw) - except Exception as err: - log.exception('Unhandled exception in worker thread') - raise err + # a[2] is the argument `destinations` + functional_destinations = a[2].functional_destinations + # In case the arguments or the method change, catch the possible exceptions + # but ignore here that there are not destinations. + except (IndexError, TypeError): + log.debug("Wrong argument or attribute.") + functional_destinations = True + if not functional_destinations or settings.end_event.is_set(): + return None + return measure_relay(*a, **kw) def _should_keep_result(did_request_maximum, result_time, download_times): @@ -300,8 +422,21 @@ def result_putter(result_dump): ''' Create a function that takes a single argument -- the measurement result -- and return that function so it can be used by someone else ''' + def closure(measurement_result): - return result_dump.queue.put(measurement_result) + # Since result_dump thread is calling queue.get() every second, + # the queue should be full for only 1 second. + # This call blocks at maximum timeout seconds. + try: + result_dump.queue.put(measurement_result, timeout=3) + except queue.Full: + # The result would be lost, the scanner will continue working. + log.warning( + "The queue with measurements is full, when adding %s.\n" + "It is possible that the thread that get them to " + "write them to the disk (ResultDump.enter) is stalled.", + measurement_result + ) return closure @@ -309,13 +444,221 @@ ''' Create a function that takes a single argument -- an error from a measurement -- and return that function so it can be used by someone else ''' - def closure(err): - log.error('Unhandled exception caught while measuring %s: %s %s', - target.nickname, type(err), err) + def closure(object): + if settings.end_event.is_set(): + return + # The only object that can be here if there is not any uncatched + # exception is stem.SocketClosed when stopping sbws + # An exception here means that the worker thread finished. + log.warning(FILLUP_TICKET_MSG) + # To print the traceback that happened in the thread, not here in + # the main process. + log.warning("".join(traceback.format_exception( + type(object), object, object.__traceback__)) + ) return closure +def main_loop(args, conf, controller, relay_list, circuit_builder, result_dump, + relay_prioritizer, destinations, max_pending_results, pool): + """Starts and reuse the threads that measure the relays forever. + + It starts a loop that will be run while there is not and event signaling + that sbws is stopping (because of SIGTERM or SIGINT). + + Then, it starts a second loop with an ordered list (generator) of relays + to measure that might a subset of all the current relays in the Network. + + For every relay, it starts a new thread which runs ``measure_relay`` to + measure the relay until there are ``max_pending_results`` threads. + After that, it will reuse a thread that has finished for every relay to + measure. + It is the the pool method ``apply_async`` which starts or reuse a thread. + This method returns an ``ApplyResult`` immediately, which has a ``ready`` + methods that tells whether the thread has finished or not. + + When the thread finish, ie. ``ApplyResult`` is ``ready``, it triggers + ``result_putter`` callback, which put the ``Result`` in ``ResultDump`` + queue and complete immediately. + + ``ResultDump`` thread (started before and out of this function) will get + the ``Result`` from the queue and write it to disk, so this doesn't block + the measurement threads. + + If there was an exception not catched by ``measure_relay``, it will call + instead ``result_putter_error``, which logs the error and complete + immediately. + + Before the outer loop iterates, it waits (non blocking) that all + the ``Results`` are ready calling ``wait_for_results``. + This avoid to start measuring the same relay which might still being + measured. + + """ + hbeat = Heartbeat(conf.getpath('paths', 'state_fname')) + + # Set the time to wait for a thread to finish as the half of an HTTP + # request timeout. + # Do not start a new loop if sbws is stopping. + while not settings.end_event.is_set(): + log.debug("Starting a new measurement loop.") + num_relays = 0 + # Since loop might finish before pending_results is 0 due waiting too + # long, set it here and not outside the loop. + pending_results = [] + loop_tstart = time.time() + + # Register relay fingerprints to the heartbeat module + hbeat.register_consensus_fprs(relay_list.relays_fingerprints) + + for target in relay_prioritizer.best_priority(): + # Don't start measuring a relay if sbws is stopping. + if settings.end_event.is_set(): + break + relay_list.increment_recent_measurement_attempt_count() + target.increment_relay_recent_measurement_attempt_count() + num_relays += 1 + # callback and callback_err must be non-blocking + callback = result_putter(result_dump) + callback_err = result_putter_error(target) + async_result = pool.apply_async( + dispatch_worker_thread, + [args, conf, destinations, circuit_builder, relay_list, + target], {}, callback, callback_err) + pending_results.append(async_result) + + # Register this measurement to the heartbeat module + hbeat.register_measured_fpr(target.fingerprint) + + # After the for has finished, the pool has queued all the relays + # and pending_results has the list of all the AsyncResults. + # It could also be obtained with pool._cache, which contains + # a dictionary with AsyncResults as items. + num_relays_to_measure = len(pending_results) + wait_for_results(num_relays_to_measure, pending_results) + + # Print the heartbeat message + hbeat.print_heartbeat_message() + + loop_tstop = time.time() + loop_tdelta = (loop_tstop - loop_tstart) / 60 + # At this point, we know the relays that were queued to be measured. + # That does not mean they were actually measured. + log.debug("Attempted to measure %s relays in %s minutes", + num_relays, loop_tdelta) + # In a testing network, exit after first loop + if controller.get_conf('TestingTorNetwork') == '1': + log.info("In a testing network, exiting after the first loop.") + # Threads should be closed nicely in some refactor + stop_threads(signal.SIGTERM, None) + + +def wait_for_results(num_relays_to_measure, pending_results): + """Wait for the pool to finish and log progress. + + While there are relays being measured, just log the progress + and sleep :const:`~sbws.globals.TIMEOUT_MEASUREMENTS` (3mins), + which is aproximately the time it can take to measure a relay in + the worst case. + + When there has not been any relay measured in ``TIMEOUT_MEASUREMENTS`` + and there are still relays pending to be measured, it means there is no + progress and call :func:`~sbws.core.scanner.force_get_results`. + + This can happen in the case of a bug that makes either + :func:`~sbws.core.scanner.measure_relay`, + :func:`~sbws.core.scanner.result_putter` (callback) and/or + :func:`~sbws.core.scanner.result_putter_error` (callback error) stall. + + .. note:: in a future refactor, this could be simpler by: + + 1. Initializing the pool at the begingging of each loop + 2. Callling :meth:`~Pool.close`; :meth:`~Pool.join` after + :meth:`~Pool.apply_async`, + to ensure no new jobs are added until the pool has finished with all + the ones in the queue. + + As currently, there would be still two cases when the pool could stall: + + 1. There's an exception in ``measure_relay`` and another in + ``callback_err`` + 2. There's an exception ``callback``. + + This could also be simpler by not having callback and callback error in + ``apply_async`` and instead just calling callback with the + ``pending_results``. + + (callback could be also simpler by not having a thread and queue and + just storing to disk, since the time to write to disk is way smaller + than the time to request over the network.) + """ + num_last_measured = 1 + while num_last_measured > 0 and not settings.end_event.is_set(): + log.info("Pending measurements: %s out of %s: ", + len(pending_results), num_relays_to_measure) + time.sleep(TIMEOUT_MEASUREMENTS) + old_pending_results = pending_results + pending_results = [r for r in pending_results if not r.ready()] + num_last_measured = len(old_pending_results) - len(pending_results) + if len(pending_results) > 0: + force_get_results(pending_results) + + +def force_get_results(pending_results): + """Try to get either the result or an exception, which gets logged. + + It is call by :func:`~sbws.core.scanner.wait_for_results` when + the time waiting for the results was long. + + To get either the :class:`~sbws.lib.resultdump.Result` or an exception, + call :meth:`~AsyncResult.get` with timeout. + Timeout is low since we already waited. + + ``get`` is not call before, because it blocks and the callbacks + are not call. + """ + log.debug("Forcing get") + for r in pending_results: + try: + result = r.get(timeout=0.1) + log.warning("Result %s was not stored, it took too long.", + result) + # TimeoutError is raised when the result is not ready, ie. has not + # been processed yet + except TimeoutError: + log.warning("A result was not stored, it was not ready.") + # If the result raised an exception, `get` returns it, + # then log any exception so that it can be fixed. + # This should not happen, since `callback_err` would have been call + # first. + except Exception as e: + log.critical(FILLUP_TICKET_MSG) + # If the exception happened in the threads, `log.exception` does + # not have the traceback. + # Using `format_exception` instead of of `print_exception` to show + # the traceback in all the log handlers. + log.warning("".join(traceback.format_exception( + type(e), e, e.__traceback__))) + + def run_speedtest(args, conf): + """Initializes all the data and threads needed to measure the relays. + + It launches or connect to Tor in a thread. + It initializes the list of relays seen in the Tor network. + It starts a thread to read the previous measurements and wait for new + measurements to write them to the disk. + It initializes a class that will be used to order the relays depending + on their measurements age. + It initializes the list of destinations that will be used for the + measurements. + It initializes the thread pool that will launch the measurement threads. + The pool starts 3 other threads that are not the measurement (worker) + threads. + Finally, it calls the function that will manage the measurement threads. + + """ + global rd, pool, controller controller, _ = stem_utils.init_controller( path=conf.getpath('tor', 'control_socket')) if not controller: @@ -332,9 +675,19 @@ 'even lead to messed up results.', conf.getpath('tor', 'control_socket')) time.sleep(15) - rl = RelayList(args, conf, controller) + + # When there will be a refactor where conf is global, this can be removed + # from here. + state = State(conf.getpath('paths', 'state_fname')) + # Call only once to initialize http_headers + settings.init_http_headers(conf.get('scanner', 'nickname'), state['uuid'], + str(controller.get_version())) + # To do not have to pass args and conf to RelayList, pass an extra + # argument with the data_period + measurements_period = conf.getint('general', 'data_period') + rl = RelayList(args, conf, controller, measurements_period, state) cb = CB(args, conf, controller, rl) - rd = ResultDump(args, conf, end_event) + rd = ResultDump(args, conf) rp = RelayPrioritizer(args, conf, rl, rd) destinations, error_msg = DestinationList.from_config( conf, cb, rl, controller) @@ -342,30 +695,18 @@ fail_hard(error_msg) max_pending_results = conf.getint('scanner', 'measurement_threads') pool = Pool(max_pending_results) - pending_results = [] - while True: - num_relays = 0 - loop_tstart = time.time() - for target in rp.best_priority(): - num_relays += 1 - log.debug('Measuring %s %s', target.nickname, - target.fingerprint[0:8]) - callback = result_putter(rd) - callback_err = result_putter_error(target) - async_result = pool.apply_async( - dispatch_worker_thread, - [args, conf, destinations, cb, rl, target], - {}, callback, callback_err) - pending_results.append(async_result) - while len(pending_results) >= max_pending_results: - time.sleep(5) - pending_results = [r for r in pending_results if not r.ready()] - while len(pending_results) > 0: - time.sleep(5) - pending_results = [r for r in pending_results if not r.ready()] - loop_tstop = time.time() - loop_tdelta = (loop_tstop - loop_tstart) / 60 - log.debug("Measured %s relays in %s minutes", num_relays, loop_tdelta) + try: + main_loop(args, conf, controller, rl, cb, rd, rp, destinations, + max_pending_results, pool) + except KeyboardInterrupt: + log.info("Interrupted by the user.") + stop_threads(signal.SIGINT, None) + # Any exception not catched at this point would make the scanner stall. + # Log it and exit gracefully. + except Exception as e: + log.critical(FILLUP_TICKET_MSG) + log.exception(e) + stop_threads(signal.SIGTERM, None, 1) def gen_parser(sub): @@ -391,10 +732,8 @@ state = State(conf.getpath('paths', 'state_fname')) state['scanner_started'] = now_isodt_str() + # Generate an unique identifier for each scanner + if 'uuid' not in state: + state['uuid'] = str(uuid.uuid4()) - try: - run_speedtest(args, conf) - except KeyboardInterrupt as e: - raise e - finally: - end_event.set() + run_speedtest(args, conf) diff -Nru sbws-1.0.2/sbws/globals.py sbws-1.1.0/sbws/globals.py --- sbws-1.0.2/sbws/globals.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/globals.py 2019-03-29 13:47:26.000000000 +0000 @@ -1,15 +1,26 @@ import os import logging -import socket +import platform + +from requests import __version__ as requests_version +from stem import __version__ as stem_version + +from sbws import __version__ + + +from collections import OrderedDict + log = logging.getLogger(__name__) RESULT_VERSION = 4 WIRE_VERSION = 1 -SPEC_VERSION = '1.2.0' +SPEC_VERSION = '1.4.0' # This is a dictionary of torrc options we always want to set when launching # Tor and that do not depend on any runtime configuration +# Options that are known at runtime (from configuration file) are added +# in utils/stem.py launch_tor TORRC_STARTING_POINT = { # We will find out via the ControlPort and not setting something static # means a lower chance of conflict @@ -21,7 +32,27 @@ # Because we need things from full server descriptors (namely for now: the # bandwidth line) 'UseMicrodescriptors': '0', + # useful logging options for clients that don't care about anonymity + 'SafeLogging': '0', + 'LogTimeGranularity': '1', + 'ProtocolWarnings': '1', } +# Options that need to be set at runtime. +TORRC_RUNTIME_OPTIONS = { + # The scanner builds the circuits to download the data itself, + # so do not let Tor to build them. + '__DisablePredictedCircuits': '1', + # The scanner attach the streams to the circuit itself, + # so do not let Tor to attache them. + '__LeaveStreamsUnattached': '1', +} +# Options that can be set at runtime and can fail with some Tor versions +# The ones that fail will be ignored.. +TORRC_OPTIONS_CAN_FAIL = OrderedDict({ + # Since currently scanner anonymity is not the goal, ConnectionPadding + # is disable to do not send extra traffic + 'ConnectionPadding': '0' + }) PKG_DIR = os.path.abspath(os.path.dirname(__file__)) DEFAULT_CONFIG_PATH = os.path.join(PKG_DIR, 'config.default.ini') @@ -31,6 +62,7 @@ SUPERVISED_RUN_DPATH = "/run/sbws/tor" SOCKET_TIMEOUT = 60 # seconds +TIMEOUT_MEASUREMENTS = 60 * 3 # 3 minutes SBWS_SCALE_CONSTANT = 7500 TORFLOW_SCALING = 1 @@ -40,6 +72,7 @@ TORFLOW_OBS_MEAN = 1 TORFLOW_OBS_DECAYING = 3 TORFLOW_ROUND_DIG = 3 +PROP276_ROUND_DIG = 2 DAY_SECS = 86400 NUM_MIN_RESULTS = 2 MIN_REPORT = 60 @@ -47,7 +80,62 @@ # in the bandwidth lines in percentage MAX_BW_DIFF_PERC = 50 -BW_LINE_SIZE = 510 +# With the new KeyValues in #29591, the lines are greater than 510 +# Tor already accept lines of any size, but leaving the limit anyway. +BW_LINE_SIZE = 1022 + +# RelayList, ResultDump, v3bwfile +# For how many seconds in the past the relays and measurements data is keep/ +# considered valid. +# This is currently set by default in config.default.ini as ``date_period``, +# and used in ResultDump and v3bwfile. +# In a future refactor, constants in config.default.ini should be moved here, +# or calculated in settings, so that there's no need to pass the configuration +# to all the functions. +MEASUREMENTS_PERIOD = 5 * 24 * 60 * 60 + +# Metadata to send in every requests, so that data servers can know which +# scanners are using them. +# In Requests these keys are case insensitive. +HTTP_HEADERS = { + # This would be ignored if changing to HTTP/2 + 'Connection': 'keep-alive', + # Needs to get Tor version from the controller + 'User-Agent': 'sbws/{} ({}) Python/{} Requests/{} Stem/{} Tor/'.format( + __version__, platform.platform(), + platform.python_version(), + requests_version, stem_version), + # Organization defined names (:rfc:`7239`) + # Needs to get the nickname from the user config file. + 'Tor-Bandwidth-Scanner-Nickname': '{}', + 'Tor-Bandwidth-Scanner-UUID': '{}', + # In case of including IP address. + # 'Forwarded': 'for={}' # IPv6 part, if there's + } +# In the case of having ipv6 it's concatenated to forwarder. +IPV6_FORWARDED = ', for="[{}]"' + +HTTP_GET_HEADERS = { + 'Range': '{}', + 'Accept-Encoding': 'identity', +} +DESTINATION_VERIFY_CERTIFICATE = True +# This number might need adjusted depending on the percentage of circuits and +# HTTP requests failures. + +# Number of attempts to use a destination, that are stored, in order to decide +# whether the destination is functional or not. +NUM_DESTINATION_ATTEMPTS_STORED = 10 +# Time to wait before trying again a destination that wasn't functional. +# Because intermitent failures with CDN destinations, start trying again +# after 5 min. +DELTA_SECONDS_RETRY_DESTINATION = 60 * 5 +# Number of consecutive times a destination can fail before considering it +# not functional. +MAX_NUM_DESTINATION_FAILURES = 3 +# By which factor to multiply DELTA_SECONDS_RETRY_DESTINATION when the +# destination fail again. +FACTOR_INCREMENT_DESTINATION_RETRY = 2 def fail_hard(*a, **kw): @@ -69,25 +157,3 @@ log.debug('Touching %s', fname) with open(fname, 'a') as fd: os.utime(fd.fileno(), times=times) - - -def resolve(hostname, ipv4_only=False, ipv6_only=False): - assert not (ipv4_only and ipv6_only) - results = [] - try: - results = socket.getaddrinfo(hostname, 0) - except socket.gaierror: - log.warn( - 'Unable to resolve %s hostname. Returning empty list of addresses', - hostname) - return [] - ret = set() - for result in results: - fam, _, _, _, addr = result - if fam == socket.AddressFamily.AF_INET6 and not ipv4_only: - ret.add(addr[0]) - elif fam == socket.AddressFamily.AF_INET and not ipv6_only: - ret.add(addr[0]) - else: - assert None, 'Unknown address family {}'.format(fam) - return list(ret) diff -Nru sbws-1.0.2/sbws/__init__.py sbws-1.1.0/sbws/__init__.py --- sbws-1.0.2/sbws/__init__.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/__init__.py 2019-03-29 13:47:26.000000000 +0000 @@ -1 +1,36 @@ -__version__ = '1.0.2' \ No newline at end of file +__version__ = '1.1.0' + +import threading # noqa + +from . import globals # noqa + + +class Settings: + """Singleton settings for all the packages. + This way change settings can be seen by all the packages that import it. + + It lives in ``__init__.py`` to leave open the possibility of having a + ``settings.py`` module for user settings. + + .. note:: After refactoring, globals should only have constants. + Any other variable that needs to be modified when initializing + should be initialized here. + + """ + def __init__(self): + # update this dict from globals (but only for ALL_CAPS settings) + for setting in dir(globals): + if setting.isupper(): + setattr(self, setting, getattr(globals, setting)) + self.end_event = threading.Event() + + def init_http_headers(self, nickname, uuid, tor_version): + self.HTTP_HEADERS['Tor-Bandwidth-Scanner-Nickname'] = nickname + self.HTTP_HEADERS['Tor-Bandwidth-Scanner-UUID'] = uuid + self.HTTP_HEADERS['User-Agent'] += tor_version + + def set_end_event(self): + self.end_event.set() + + +settings = Settings() # noqa diff -Nru sbws-1.0.2/sbws/lib/circuitbuilder.py sbws-1.1.0/sbws/lib/circuitbuilder.py --- sbws-1.0.2/sbws/lib/circuitbuilder.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/lib/circuitbuilder.py 2019-03-29 13:47:26.000000000 +0000 @@ -1,5 +1,5 @@ from stem import CircuitExtensionFailed, InvalidRequest, ProtocolError, Timeout -from stem import InvalidArguments, ControllerError +from stem import InvalidArguments, ControllerError, SocketClosed import random from .relaylist import Relay import logging @@ -7,20 +7,8 @@ log = logging.getLogger(__name__) -class PathLengthException(Exception): - def __init__(self, message=None, errors=None): - if message is not None: - super().__init__(message) - else: - super().__init__() - self.errors = errors - - def valid_circuit_length(path): - assert isinstance(path, int) or isinstance(path, list) - if isinstance(path, int): - return path > 0 and path <= 8 - return len(path) > 0 and len(path) <= 8 + return 0 < len(path) <= 8 class CircuitBuilder: @@ -56,35 +44,31 @@ raise NotImplementedError() def close_circuit(self, circ_id): - c = self.controller try: - c.get_circuit(circ_id, default=None) - try: - c.close_circuit(circ_id) - except (InvalidArguments, InvalidRequest): - pass - self.built_circuits.discard(circ_id) - except (ControllerError, ValueError) as e: - log.exception("Error trying to get circuit to close it: %s.", e) + self.controller.close_circuit(circ_id) + # SocketClosed will be raised when stopping sbws + except (InvalidArguments, InvalidRequest, SocketClosed) as e: + log.debug(e) + self.built_circuits.discard(circ_id) def _build_circuit_impl(self, path): + """ + :returns tuple: circuit id if the circuit was built, error if there + was an error building the circuit. + """ if not valid_circuit_length(path): - raise PathLengthException() + return None, "Can not build a circuit, invalid path." c = self.controller timeout = self.circuit_timeout - fp_path = '[' + ' -> '.join([p[0:8] for p in path]) + ']' + fp_path = '[' + ' -> '.join([p for p in path]) + ']' log.debug('Building %s', fp_path) - for _ in range(0, 3): - try: - circ_id = c.new_circuit( - path, await_build=True, timeout=timeout) - except (InvalidRequest, CircuitExtensionFailed, - ProtocolError, Timeout) as e: - log.warning(e) - continue - else: - return circ_id - return None + try: + circ_id = c.new_circuit( + path, await_build=True, timeout=timeout) + except (InvalidRequest, CircuitExtensionFailed, + ProtocolError, Timeout, SocketClosed) as e: + return None, str(e) + return circ_id, None def __del__(self): c = self.controller @@ -103,6 +87,9 @@ self.built_circuits.clear() +# In a future refactor, remove this class, since sbws chooses the relays to +# build the circuit, the relays are not just choosen as random as this class +# does. class GapsCircuitBuilder(CircuitBuilder): ''' The build_circuit member function takes a list. Falsey values in the list will be replaced with relays chosen uniformally at random; Truthy @@ -149,19 +136,16 @@ chosen uniformally at random. A relay will not be in a circuit twice. ''' if not valid_circuit_length(path): - raise PathLengthException() + return None, "Can not build a circuit, invalid path." path = self._normalize_path(path) if path is None: - return None + return None, "Can not build a circuit, no path." num_missing = len(['foo' for r in path if not r]) insert_relays = self._random_sample_relays( num_missing, [r for r in path if r is not None]) if insert_relays is None: path = ','.join([r.nickname if r else str(None) for r in path]) - log.warning( - 'Problem building a circuit to satisfy %s with available ' - 'relays in the network', path) - return None + return None, "Can not build a circuit with the current relays." assert len(insert_relays) == num_missing path = [r.fingerprint if r else insert_relays.pop().fingerprint for r in path] diff -Nru sbws-1.0.2/sbws/lib/destination.py sbws-1.1.0/sbws/lib/destination.py --- sbws-1.0.2/sbws/lib/destination.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/lib/destination.py 2019-03-29 13:47:26.000000000 +0000 @@ -1,22 +1,50 @@ +import collections +import datetime import logging import random -import time -import os -from threading import RLock import requests from urllib.parse import urlparse from stem.control import EventType + +from sbws.globals import DESTINATION_VERIFY_CERTIFICATE import sbws.util.stem as stem_utils -import sbws.util.requests as requests_utils +from ..globals import ( + MAX_NUM_DESTINATION_FAILURES, + DELTA_SECONDS_RETRY_DESTINATION, + NUM_DESTINATION_ATTEMPTS_STORED, + FACTOR_INCREMENT_DESTINATION_RETRY + ) +from sbws import settings + log = logging.getLogger(__name__) +# Duplicate some code from DestinationList.from_config, +# it should be refactored. +def parse_destinations_countries(conf): + """Returns the destinations' country as string separated by comma. + + """ + destinations_countries = [] + for key in conf['destinations'].keys(): + # Not a destination key + if key in ['usability_test_interval']: + continue + # The destination is not enabled + if not conf['destinations'].getboolean(key): + continue + destination_section = 'destinations.{}'.format(key) + destination_country = conf[destination_section].get('country', None) + destinations_countries.append(destination_country) + return ','.join(destinations_countries) + + def _parse_verify_option(conf_section): if 'verify' not in conf_section: - return True + return DESTINATION_VERIFY_CERTIFICATE try: - return conf_section.getboolean('verify') + verify = conf_section.getboolean('verify') except ValueError: log.warning( 'Currently sbws only supports verify=true/false, not a CA bundle ' @@ -26,6 +54,11 @@ 'of testing. So we will allow this, but expect Requests to throw ' 'SSLError exceptions later. Have fun!', conf_section['verify']) return conf_section['verify'] + if not verify: + # disable urllib3 warning: InsecureRequestWarning + import urllib3 + urllib3.disable_warnings() + return verify def connect_to_destination_over_circuit(dest, circ_id, session, cont, max_dl): @@ -66,54 +99,171 @@ should commence. False and an error string otherwise. ''' assert isinstance(dest, Destination) + log.debug("Connecting to destination over circuit.") + # Do not start if sbws is stopping + if settings.end_event.is_set(): + return False, "Shutting down." error_prefix = 'When sending HTTP HEAD to {}, '.format(dest.url) with stem_utils.stream_building_lock: listener = stem_utils.attach_stream_to_circuit_listener(cont, circ_id) stem_utils.add_event_listener(cont, listener, EventType.STREAM) try: - # TODO: - # - What other exceptions can this throw? - head = requests_utils.head(session, dest.url, verify=dest.verify) - except (requests.exceptions.ConnectionError, - requests.exceptions.ReadTimeout) as e: + head = session.head(dest.url, verify=dest.verify) + except requests.exceptions.RequestException as e: + dest.add_failure() return False, 'Could not connect to {} over circ {} {}: {}'.format( dest.url, circ_id, stem_utils.circuit_str(cont, circ_id), e) finally: stem_utils.remove_event_listener(cont, listener) if head.status_code != requests.codes.ok: + dest.add_failure() return False, error_prefix + 'we expected HTTP code '\ '{} not {}'.format(requests.codes.ok, head.status_code) if 'content-length' not in head.headers: + dest.add_failure() return False, error_prefix + 'we except the header Content-Length '\ - 'to exist in the response' + 'to exist in the response' content_length = int(head.headers['content-length']) if max_dl > content_length: + dest.add_failure() return False, error_prefix + 'our maximum configured download size '\ 'is {} but the content is only {}'.format(max_dl, content_length) log.debug('Connected to %s over circuit %s', dest.url, circ_id) + # Any failure connecting to the destination will call add_failure, + # It can not be set at the start, to be able to know whether it is + # failing consecutive times. + dest.add_success() return True, {'content_length': content_length} class Destination: - def __init__(self, url, max_dl, verify): + """Web server from which data is downloaded to measure bandwidth. + """ + # NOTE: max_dl and verify should be optional and have defaults + def __init__(self, url, max_dl, verify, + max_num_failures=MAX_NUM_DESTINATION_FAILURES, + delta_seconds_retry=DELTA_SECONDS_RETRY_DESTINATION, + num_attempts_stored=NUM_DESTINATION_ATTEMPTS_STORED, + factor_increment_retry=FACTOR_INCREMENT_DESTINATION_RETRY): + """Initalizes the Web server from which the data is downloaded. + + :param str url: Web server data URL to download. + :param int max_dl: Maximum size of the the data to download. + :param bool verify: Whether to verify or not the TLS certificate. + :param int max_num_failures: Number of consecutive failures when the + destination is not considered functional. + :param int delta_seconds_retry: Delta time to try a destination + that was not functional. + :param int num_attempts_stored: Number of attempts to store. + :param int factor_increment_retry: Factor to increment delta by + before trying to use a destination again. + """ self._max_dl = max_dl u = urlparse(url) - # these things should have been verified in verify_config - assert u.scheme in ['http', 'https'] - assert u.netloc self._url = u self._verify = verify - def is_usable(self, circ_id, session, cont): - ''' Use **connect_to_destination_over_circuit** to determine if this - destination is usable and return what it returns. Just a small wrapper. - ''' - if not isinstance(self.verify, bool): - if not os.path.isfile(self.verify): - return False, '{} is believed to be a CA bundle file on disk '\ - 'but it does not exist'.format(self.verify) - return connect_to_destination_over_circuit( - self, circ_id, session, cont, self._max_dl) + # Attributes to decide whether a destination is functional or not. + self._max_num_failures = max_num_failures + self._num_attempts_stored = num_attempts_stored + # Default delta time to try a destination that was not functional. + self._default_delta_seconds_retry = delta_seconds_retry + self._delta_seconds_retry = delta_seconds_retry + # Using a deque (FIFO) to do not grow forever and + # to do not have to remove old attempts. + # Store tuples of timestamp and whether the destination succed or not + # (succed, 1, failed, 0). + # Initialize it as if it never failed. + self._attempts = collections.deque([(datetime.datetime.utcnow(), 1), ], + maxlen=self._num_attempts_stored) + self._factor = factor_increment_retry + + def _last_attempts(self, n=None): + """Return the last ``n`` attempts the destination was used.""" + # deque does not accept slices, + # a new deque is returned with the last n items + # (or less if there were less). + return collections.deque(self._attempts, + maxlen=(n or self._max_num_failures)) + + def _are_last_attempts_failures(self, n=None): + """ + Return True if the last`` n`` times the destination was used + and failed. + """ + # Count the number that there was a failure when used + n = n if n else self._max_num_failures + return ([i[1] for i in self._last_attempts(n)].count(0) + >= self._max_num_failures) + + def _increment_time_to_retry(self, factor=None): + """ + Increment the time a destination will be tried again by a ``factor``. + """ + self._delta_seconds_retry *= factor or self._factor + log.info("Incremented the time to try destination %s to %s hours.", + self.url, self._delta_seconds_retry / 60 / 60) + + def _is_last_try_old_enough(self, n=None): + """ + Return True if the last time it was used it was ``n`` seconds ago. + """ + # Timestamp of the last attempt. + last_time = self._attempts[-1][0] + # If the last attempt is older than _delta_seconds_retry, try again + return (datetime.datetime.utcnow() + - datetime.timedelta(seconds=self._delta_seconds_retry) + > last_time) + return False + + def is_functional(self): + """Whether connections to a destination are failing or not. + + Return True if: + - It did not fail more than n (by default 3) consecutive times. + - The last time the destination was tried + was x (by default 3h) seconds ago. + And False otherwise. + + When the destination is tried again after the consecutive failures, + the time to try again is incremented and resetted as soon as the + destination does not fail. + """ + # NOTE: does a destination fail because several threads are using + # it at the same time? + # If a destination fails for 1 minute and there're 3 threads, the + # 3 threads will fail. + + # Failed the last X consecutive times + if self._are_last_attempts_failures(): + # The log here will appear in all the the queued + # relays and threads. + log.warning("The last %s times the destination %s failed." + "Disabled for %s minutes.", + self._max_num_failures, self.url, + self._delta_seconds_retry / 60) + log.warning("Please, add more destinations or increment the " + "number of maximum number of consecutive failures " + "in the configuration.") + # It was not used for a while and the last time it was used + # was long ago, then try again + if self._is_last_try_old_enough(): + log.info("The destination %s was not tried for %s hours, " + "it is going to by tried again.") + # Set the next time to retry higher, in case this attempt fails + self._increment_time_to_retry() + return True + return False + # Reset the time to retry to the initial value + # In case it was incrememented + self._delta_seconds_retry = self._default_delta_seconds_retry + return True + + def add_failure(self, dt=None): + self._attempts.append((dt or datetime.datetime.utcnow(), 0)) + + def add_success(self, dt=None): + self._attempts.append((dt or datetime.datetime.utcnow(), 1)) @property def url(self): @@ -142,11 +292,22 @@ return p @staticmethod - def from_config(conf_section, max_dl): + def from_config(conf_section, max_dl, number_threads): assert 'url' in conf_section url = conf_section['url'] verify = _parse_verify_option(conf_section) - return Destination(url, max_dl, verify) + try: + # Because one a destination fails, all the threads that are using + # it at that moment will fail too, multiply by the number of + # threads. + max_num_failures = (conf_section.getint('max_num_failures') + or MAX_NUM_DESTINATION_FAILURES) + except ValueError: + # If the operator did not setup the number, set to the default. + max_num_failures = MAX_NUM_DESTINATION_FAILURES + + max_num_failures *= number_threads + return Destination(url, max_dl, verify, max_num_failures) class DestinationList: @@ -159,62 +320,10 @@ self._cb = circuit_builder self._rl = relay_list self._all_dests = dests - self._usable_dests = [] - self._last_usability_test = 0 - self._usability_test_interval = \ - conf.getint('destinations', 'usability_test_interval') - self._usability_test_timeout = \ - conf.getfloat('general', 'http_timeout') - self._usability_lock = RLock() - - def _should_perform_usability_test(self): - return self._last_usability_test + self._usability_test_interval <\ - time.time() - - def _perform_usability_test(self): - self._usability_lock.acquire() - log.debug('Perform usability tests') - cont = self._cont - timeout = self._usability_test_timeout - session = requests_utils.make_session(cont, timeout) - usable_dests = [] - for dest in self._all_dests: - possible_exits = [e for e in self._rl.exits - if e.can_exit_to(dest.hostname, dest.port)] - # Keep the fastest 10% of exits, or 3, whichever is larger - num_keep = int(max(3, len(possible_exits) * 0.1)) - possible_exits = sorted( - possible_exits, key=lambda e: e.bandwidth, reverse=True) - exits = possible_exits[0:num_keep] - if len(exits) < 1: - log.warning("There are no exits to perform usability tests.") - continue - # Try three times to build a circuit to test this destination - circ_id = None - for _ in range(0, 3): - # Pick a random exit - exit = self._rng.choice(exits) - circ_id = self._cb.build_circuit([None, exit.fingerprint]) - if circ_id: - break - if not circ_id: - log.warning('Unable to build a circuit to test the usability ' - 'of %s. Assuming it isn\'t usable.', dest.url) - continue - log.debug('Built circ %s %s to test usability of %s', circ_id, - stem_utils.circuit_str(cont, circ_id), dest.url) - is_usable, data = dest.is_usable(circ_id, session, cont) - if not is_usable: - log.warning(data) - self._cb.close_circuit(circ_id) - continue - assert is_usable - log.debug('%s seems usable so we will keep it', dest.url) - usable_dests.append(dest) - self._cb.close_circuit(circ_id) - self._usable_dests = usable_dests - self._last_usability_test = time.time() - self._usability_lock.release() + + @property + def functional_destinations(self): + return [d for d in self._all_dests if d.is_functional()] @staticmethod def from_config(conf, circuit_builder, relay_list, controller): @@ -232,7 +341,10 @@ log.debug('Loading info for destination %s', key) dests.append(Destination.from_config( conf[dest_sec], - conf.getint('scanner', 'max_download_size'))) + # Multiply by the number of threads since all the threads will + # fail at the same time. + conf.getint('scanner', 'max_download_size'), + conf.getint('scanner', 'measurement_threads'))) if len(dests) < 1: msg = 'No enabled destinations in config. Please see '\ 'docs/source/man_sbws.ini.rst" or "man 5 sbws.ini" ' \ @@ -245,23 +357,14 @@ ''' Returns the next destination that should be used in a measurement ''' - with self._usability_lock: - while True: - if self._should_perform_usability_test(): - self._perform_usability_test() - log.debug('%s/%s of our configured destinations are ' - 'usable at this time', len(self._usable_dests), - len(self._all_dests)) - if len(self._usable_dests) > 0: - break - time_till_next_check = self._usability_test_interval + 0.0001 - log.warning( - 'Of our %d configured destinations, none are usable at ' - 'this time. Sleeping %f seconds on this blocking call ' - 'to DestinationList.next() until we can check for a ' - 'usable destination again.', len(self._all_dests), - time_till_next_check) - time.sleep(time_till_next_check) - - self._rng.shuffle(self._usable_dests) - return self._usable_dests[0] + # Do not perform usability tests since a destination is already proven + # usable or not in every measurement, and it should depend on a X + # number of failures. + # This removes the need for an extra lock for every measurement. + # Do not change the order of the destinations, just return a + # destination. + # random.choice raises IndexError with an empty list. + if self.functional_destinations: + return self._rng.choice(self.functional_destinations) + else: + return None diff -Nru sbws-1.0.2/sbws/lib/heartbeat.py sbws-1.1.0/sbws/lib/heartbeat.py --- sbws-1.0.2/sbws/lib/heartbeat.py 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/sbws/lib/heartbeat.py 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,69 @@ +""" +Classes and functions to implement a heartbeat system to monitor the progress. +""" +import logging +import time + +from ..util.state import State + + +log = logging.getLogger(__name__) + + +class Heartbeat(object): + """ + Tracks current status of sbws and is capable of printing periodic + information about the current state + """ + + def __init__(self, state_path): + # Variable to count total progress in the last days: + # In case it is needed to see which relays are not being measured, + # store their fingerprint, not only their number. + self.measured_fp_set = set() + self.consensus_fp_set = set() + self.measured_percent = 0 + self.main_loop_tstart = time.monotonic() + + self.state_dict = State(state_path) + + self.previous_measurement_percent = 0 + + def register_measured_fpr(self, async_result): + self.measured_fp_set.add(async_result) + + def register_consensus_fprs(self, relay_fprs): + for r in relay_fprs: + self.consensus_fp_set.add(r) + + def print_heartbeat_message(self): + """Print the new percentage of the different relays that were measured. + + This way it can be known whether the scanner is making progress + measuring all the Network. + + Log the percentage, the number of relays measured and not measured, + the number of loops and the time elapsed since it started measuring. + """ + loops_count = self.state_dict.get('recent_priority_list_count', 0) + + not_measured_fp_set = self.consensus_fp_set.difference( + self.measured_fp_set + ) + main_loop_tdelta = (time.monotonic() - self.main_loop_tstart) / 60 + new_measured_percent = round( + len(self.measured_fp_set) / len(self.consensus_fp_set) * 100 + ) + + log.info("Run %s main loops.", loops_count) + log.info("Measured in total %s (%s%%) unique relays in %s minutes", + len(self.measured_fp_set), new_measured_percent, + main_loop_tdelta) + log.info("%s relays still not measured.", len(not_measured_fp_set)) + + # The case when it is equal will only happen when all the relays + # have been measured. + if (new_measured_percent <= self.previous_measurement_percent): + log.warning("There is no progress measuring new unique relays.") + + self.previous_measurement_percent = new_measured_percent diff -Nru sbws-1.0.2/sbws/lib/relaylist.py sbws-1.1.0/sbws/lib/relaylist.py --- sbws-1.0.2/sbws/lib/relaylist.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/lib/relaylist.py 2019-03-29 13:47:26.000000000 +0000 @@ -1,19 +1,52 @@ +import copy +from datetime import datetime, timedelta + from stem.descriptor.router_status_entry import RouterStatusEntryV3 from stem.descriptor.server_descriptor import ServerDescriptor from stem import Flag, DescriptorUnavailable, ControllerError -from stem.util.connection import is_valid_ipv4_address -from stem.util.connection import is_valid_ipv6_address import random -import time import logging -from sbws.globals import resolve from threading import Lock +from ..globals import MEASUREMENTS_PERIOD + log = logging.getLogger(__name__) +def remove_old_consensus_timestamps( + consensus_timestamps, measurements_period=MEASUREMENTS_PERIOD): + """ + Remove the consensus timestamps that are older than period for which + the measurements are keep from a list of consensus_timestamps. + + :param list consensus_timestamps: + :param int measurements_period: + :returns list: a new list of ``consensus_timestamps`` + """ + oldest_date = datetime.utcnow() - timedelta(measurements_period) + new_consensus_timestamps = \ + [t for t in consensus_timestamps if t >= oldest_date] + return new_consensus_timestamps + + +def valid_after_from_network_statuses(network_statuses): + """Obtain the consensus Valid-After datetime from the ``document`` + attribute of a ``stem.descriptor.RouterStatusEntryV3``. + + :param list network_statuses: + returns datetime: + """ + for ns in network_statuses: + document = getattr(ns, 'document', None) + if document: + valid_after = getattr(document, 'valid_after', None) + if valid_after: + return valid_after + return datetime.utcnow().replace(microsecond=0) + + class Relay: - def __init__(self, fp, cont, ns=None, desc=None): + def __init__(self, fp, cont, ns=None, desc=None, timestamp=None): ''' Given a relay fingerprint, fetch all the information about a relay that sbws currently needs and store it in this class. Acts as an abstraction @@ -21,6 +54,9 @@ :param str fp: fingerprint of the relay. :param cont: active and valid stem Tor controller connection + + :param datatime timestamp: the timestamp of a consensus + (RouterStatusEntryV3) from which this relay has been obtained. ''' assert isinstance(fp, str) assert len(fp) == 40 @@ -41,18 +77,24 @@ self._desc = cont.get_server_descriptor(fp, default=None) except (DescriptorUnavailable, ControllerError) as e: log.exception("Exception trying to get desc %s", e) + self._consensus_timestamps = [] + self._add_consensus_timestamp(timestamp) + # The number of times that a relay is "prioritized" to be measured. + # It is incremented in ``RelayPrioritizer.best_priority`` + self.relay_recent_priority_list_count = 0 + # The number of times that a relay has been queued to be measured. + # It is incremented in ``scanner.main_loop`` + self.relay_recent_measurement_attempt_count = 0 def _from_desc(self, attr): if not self._desc: return None - assert hasattr(self._desc, attr) - return getattr(self._desc, attr) + return getattr(self._desc, attr, None) def _from_ns(self, attr): if not self._ns: return None - assert hasattr(self._ns, attr) - return getattr(self._ns, attr) + return getattr(self._ns, attr, None) @property def nickname(self): @@ -75,12 +117,28 @@ return self._from_desc('average_bandwidth') @property + def burst_bandwidth(self): + return self._from_desc('burst_bandwidth') + + @property def observed_bandwidth(self): return self._from_desc('observed_bandwidth') @property - def bandwidth(self): - return self._from_ns('bandwidth') + def consensus_bandwidth(self): + """Return the consensus bandwidth in Bytes. + + Consensus bandwidth is the only bandwidth value that is in kilobytes. + """ + if self._from_ns('bandwidth') is not None: + return self._from_ns('bandwidth') * 1000 + + @property + def consensus_bandwidth_is_unmeasured(self): + # measured appears only votes, unmeasured appears in consensus + # therefore is_unmeasured is needed to know whether the bandwidth + # value in consensus is comming from bwauth measurements or not. + return self._from_ns('is_unmeasured') @property def address(self): @@ -101,31 +159,121 @@ return None return key.rstrip('=') - def can_exit_to(self, host, port): - ''' - Returns if this relay can MOST LIKELY exit to the given host:port. - **host** can be a hostname, but be warned that we will resolve it - locally and use the first (arbitrary/unknown order) result when - checking exit policies, which is different than what other parts of the - code may do (leaving it up to the exit to resolve the name). - ''' - if not self.exit_policy: - return False - assert isinstance(host, str) + @property + def consensus_valid_after(self): + """Obtain the consensus Valid-After from the document of this relay + network status. + """ + network_status_document = self._from_ns('document') + if network_status_document: + return getattr(network_status_document, 'valid_after', None) + return None + + @property + def last_consensus_timestamp(self): + if len(self._consensus_timestamps) >= 1: + return self._consensus_timestamps[-1] + return None + + def _add_consensus_timestamp(self, timestamp=None): + """Add the consensus timestamp in which this relay is present. + """ + # It is possible to access to the relay's consensensus Valid-After + if self.consensus_valid_after is not None: + # The consensus timestamp list was initialized. + if self.last_consensus_timestamp is not None: + # Valid-After is more recent than the most recent stored + # consensus timestamp. + if self.consensus_valid_after > self.last_consensus_timestamp: + # Add Valid-After + self._consensus_timestamps.append( + self.consensus_valid_after + ) + # The consensus timestamp list was not initialized. + else: + # Add Valid-After + self._consensus_timestamps.append(self.consensus_valid_after) + # If there was already a list the timestamp arg is more recent than + # the most recent timestamp stored, + elif (self.last_consensus_timestamp is not None + and timestamp > self.last_consensus_timestamp): + # Add the arg timestamp. + self._consensus_timestamps.append(timestamp) + # In any other case + else: + # Add the current datetime + self._consensus_timestamps.append( + datetime.utcnow().replace(microsecond=0)) + + def _remove_old_consensus_timestamps( + self, measurements_period=MEASUREMENTS_PERIOD): + self._consensus_timestamps = \ + remove_old_consensus_timestamps( + copy.deepcopy(self._consensus_timestamps), measurements_period + ) + + def update_consensus_timestamps(self, timestamp=None): + self._add_consensus_timestamp(timestamp) + self._remove_old_consensus_timestamps() + + @property + def relay_in_recent_consensus_count(self): + """Number of times the relay was in a conensus.""" + return len(self._consensus_timestamps) + + def can_exit_to_port(self, port): + """ + Returns True if the relay has an exit policy and the policy accepts + exiting to the given portself or False otherwise. + """ assert isinstance(port, int) - if not is_valid_ipv4_address(host) and not is_valid_ipv6_address(host): - # It certainly isn't perfect trying to guess if an exit can connect - # to an ipv4/6 address based on the DNS result we got locally. But - # it's the best we can do. - # - # Also, only use the first ipv4/6 we get even if there is more than - # one. - results = resolve(host) - if not len(results): - return False - host = results[0] - assert is_valid_ipv4_address(host) or is_valid_ipv6_address(host) - return self.exit_policy.can_exit_to(host, port) + # if dind't get the descriptor, there isn't exit policy + # When the attribute is gotten in getattr(self._desc, "exit_policy"), + # is possible that stem's _input_rules is None and raises an exception + # (#29899): + # File "/usr/lib/python3/dist-packages/sbws/lib/relaylist.py", line 117, in can_exit_to_port # noqa + # if not self.exit_policy: + # File "/usr/lib/python3/dist-packages/stem/exit_policy.py", line 512, in __len__ # noqa + # return len(self._get_rules()) + # File "/usr/lib/python3/dist-packages/stem/exit_policy.py", line 464, in _get_rules # noqa + # for rule in decompressed_rules: + # TypeError: 'NoneType' object is not iterable + # Therefore, catch the exception here. + try: + if self.exit_policy: + return self.exit_policy.can_exit_to(port=port) + except TypeError: + return False + return False + + def is_exit_not_bad_allowing_port(self, port): + return (Flag.BADEXIT not in self.flags and + Flag.EXIT in self.flags and + self.can_exit_to_port(port)) + + def increment_relay_recent_measurement_attempt_count(self): + """ + Increment The number of times that a relay has been queued + to be measured. + + It is call from :funf:`~sbws.core.scaner.main_loop`. + """ + # If it was not in the previous measurements version, start counting + if self.relay_recent_measurement_attempt_count is None: + self.relay_recent_measurement_attempt_count = 0 + self.relay_recent_measurement_attempt_count += 1 + + def increment_relay_recent_priority_list_count(self): + """ + The number of times that a relay is "prioritized" to be measured. + + It is call from + :meth:`~sbws.lib.relayprioritizer.RelayPrioritizer.best_priority`. + """ + # If it was not in the previous measurements version, start counting + if self.relay_recent_priority_list_count is None: + self.relay_recent_priority_list_count = 0 + self.relay_recent_priority_list_count += 1 class RelayList: @@ -133,16 +281,45 @@ transparently in the background. Provides useful interfaces for getting only relays of a certain type. ''' - REFRESH_INTERVAL = 300 # seconds - def __init__(self, args, conf, controller): + def __init__(self, args, conf, controller, + measurements_period=MEASUREMENTS_PERIOD, state=None): self._controller = controller self.rng = random.SystemRandom() self._refresh_lock = Lock() + # To track all the consensus seen. + self._consensus_timestamps = [] + # Initialize so that there's no error trying to access to it. + # In future refactor, change to a dictionary, where the keys are + # the relays' fingerprint. + self._relays = [] + # The period of time for which the measurements are keep. + self._measurements_period = measurements_period + self._state = state + # NOTE: blocking: writes to disk + if self._state: + if self._state.get('recent_measurement_attempt_count', None) \ + is None: + self._state['recent_measurement_attempt_count'] = 0 self._refresh() def _need_refresh(self): - return time.time() >= self._last_refresh + self.REFRESH_INTERVAL + # New consensuses happen every hour. + return datetime.utcnow() >= \ + self.last_consensus_timestamp + timedelta(seconds=60*60) + + @property + def last_consensus_timestamp(self): + """Returns the datetime when the last consensus was obtained.""" + if (getattr(self, "_consensus_timestamps") + and self._consensus_timestamps): + return self._consensus_timestamps[-1] + # If the object was not created from __init__, it won't have + # consensus_timestamps attribute or it might be empty. + # In this case force new update. + # Anytime more than 1h in the past will be old. + self._consensus_timestamps = [] + return datetime.utcnow() - timedelta(seconds=60*61) @property def relays(self): @@ -192,6 +369,13 @@ def authorities(self): return self._relays_with_flag(Flag.AUTHORITY) + @property + def relays_fingerprints(self): + # Using relays instead of _relays, so that the list get updated if + # needed, since this method is used to know which fingerprints are in + # the consensus. + return [r.fingerprint for r in self.relays] + def random_relay(self): return self.rng.choice(self.relays) @@ -201,16 +385,76 @@ def _relays_without_flag(self, flag): return [r for r in self.relays if flag not in r.flags] + def _remove_old_consensus_timestamps(self): + self._consensus_timestamps = remove_old_consensus_timestamps( + copy.deepcopy(self._consensus_timestamps), + self._measurements_period + ) + def _init_relays(self): + """Returns a new list of relays that are in the current consensus. + And update the consensus timestamp list with the current one. + + """ c = self._controller - try: - relays = [Relay(ns.fingerprint, c, ns=ns) - for ns in c.get_network_statuses()] - except ControllerError as e: - log.exception("Exception trying to init relays %s", e) - return [] - return relays + # This will get router statuses from this Tor cache, might not be + # updated with the network. + # Change to stem.descriptor.remote in future refactor. + network_statuses = c.get_network_statuses() + new_relays_dict = dict([(r.fingerprint, r) for r in network_statuses]) + + # Find the timestamp of the last consensus. + timestamp = valid_after_from_network_statuses(network_statuses) + self._consensus_timestamps.append(timestamp) + self._remove_old_consensus_timestamps() + # Update the relays that were in the previous consensus with the + # new timestamp + new_relays = [] + relays = copy.deepcopy(self._relays) + for r in relays: + if r.fingerprint in new_relays_dict.keys(): + r.update_consensus_timestamps(timestamp) + new_relays_dict.pop(r.fingerprint) + new_relays.append(r) + + # Add the relays that were not in the previous consensus + # If there was an relay in some older previous consensus, + # it won't get stored, so its previous consensuses are lost, + # but probably this is fine for now to don't make it more complicated. + for fp, ns in new_relays_dict.items(): + r = Relay(ns.fingerprint, c, ns=ns, timestamp=timestamp) + new_relays.append(r) + return new_relays def _refresh(self): + # Set a new list of relays. self._relays = self._init_relays() - self._last_refresh = time.time() + + log.info("Number of consensuses obtained in the last %s days: %s.", + int(self._measurements_period / 24 / 60 / 60), + self.recent_consensus_count) + # NOTE: blocking, writes to file! + if self._state is not None: + self._state['recent_consensus_count'] = self.recent_consensus_count + + @property + def recent_consensus_count(self): + """Number of times a new consensus was obtained.""" + return len(self._consensus_timestamps) + + def exits_not_bad_allowing_port(self, port): + return [r for r in self.exits + if r.is_exit_not_bad_allowing_port(port)] + + def increment_recent_measurement_attempt_count(self): + """ + Increment the number of times that any relay has been queued to be + measured. + + It is call from :funf:`~sbws.core.scaner.main_loop`. + + It is read and stored in a ``state`` file. + """ + # NOTE: blocking, writes to file! + if self._state: + self._state['recent_measurement_attempt_count'] += 1 diff -Nru sbws-1.0.2/sbws/lib/relayprioritizer.py sbws-1.1.0/sbws/lib/relayprioritizer.py --- sbws-1.0.2/sbws/lib/relayprioritizer.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/lib/relayprioritizer.py 2019-03-29 13:47:26.000000000 +0000 @@ -1,12 +1,13 @@ from decimal import Decimal from ..lib.resultdump import ResultDump -from ..lib.resultdump import Result from ..lib.resultdump import ResultError from ..lib.relaylist import RelayList import copy import time import logging +from ..util import state + log = logging.getLogger(__name__) @@ -22,33 +23,81 @@ self.min_to_return = conf.getint('relayprioritizer', 'min_relays') self.fraction_to_return = conf.getfloat( 'relayprioritizer', 'fraction_relays') + self._state = state.State(conf.getpath('paths', 'state_fname')) + if self._state is not None: + # If it was not in previous state versions, initialize it to 0 + if self._state.get('recent_priority_list_count', None) is None: + self._state['recent_priority_list_count'] = 0 + if self._state.get('recent_priority_relay_count') is None: + self._state['recent_priority_relay_count'] = 0 + + def increment_priority_lists(self): + """ + Increment the number of times that + :meth:`~sbws.lib.relayprioritizer.RelayPrioritizer.best_priority` + has been run. + """ + # NOTE: blocking, writes to file! + self._state['recent_priority_list_count'] += 1 + + def increment_priority_relays(self, relays_count): + """ + Increment the number of relays that have been "prioritized" to be + measured in a + :meth:`~sbws.lib.relayprioritizer.RelayPrioritizer.best_priority`. + """ + # NOTE: blocking, writes to file! + self._state['recent_priority_relay_count'] += relays_count + + def best_priority(self, prioritize_result_error=False, + return_fraction=True): + """Yields a new ordered list of relays to be measured next. + + The relays that were measured farther away in the past, + get prioritized (lowest priority number, first in the list). + The relays that were measured more recently get lower priority (last in + the list, higher priority number). + + Optionally, the relays which measurements failed can be prioritized + (first in the list). + However, unstable relays that fail often to be measured, might fail + again and stable relays will get measured only when their measurements + become old enough. + The opposite might be more suitable: give lower priority to the relays + that are unstable, to don't spend time measuring relays that might fail + to be measured. + + Optionally, return only a fraction of all the relays in the network. + Since there could be new relays in the network while measuring the + list of relays returned by this method, this method is run again + before all the relays in the network are measured. + + .. note:: + + In a future refactor, instead of having a static fraction of relays + to be measured, this method could be call when it's known that + there're X number of new relays in the network. + + Since measurements made before than X days ago (too old) are not + considered, and the initial list of past measurements is only filtered + when the scanner starts, it's needed to filter here again to discard + those measurements. + + :param bool prioritize_result_error: whether prioritize or not + measurements that did not succed. + :param bool return_fraction: whether to return only a fraction of the + relays seen in the network or return all. + return: a generator of the new ordered list of relays to measure next. - def best_priority(self): - ''' Return a generator containing the best priority relays. - - NOTE: A lower value for priority means better priority. Remember your - data structures class in university and consider this something like a - min-priority queue. - - Priority is calculated as the sum of the "freshness" of each - result for a relay. First we determine , the time at - which we stop considering results to be valid. From there, a result's - freshness is determined to be the amount of time between when the - measurement was made and . Therefore, you should see - that a measurement made more recently will have a higher freshness. - - We adjust down the freshness for results containing errors. If we - ignored errors and didn't increase a relay's priority value for them, - then we'll get stuck trying to measure a few relays that have the best - priority but are having issues getting measured. If we treated errors - with equal weight as successful results, then it would take a while to - get around to giving the relay another chance at a getting a successful - measurement. - ''' + """ fn_tstart = Decimal(time.time()) relays = set(copy.deepcopy(self.relay_list.relays)) if not self.measure_authorities: relays = relays.difference(set(self.relay_list.authorities)) + # Since there will be new measurements every time this method is called + # again, update the list of results. + # In a future refactor with other data structure there should not be + # needed. rd = self.result_dump for relay in relays: results = rd.results_for_relay(relay) @@ -56,38 +105,54 @@ # The time before which we do not consider results valid anymore oldest_allowed = time.time() - self.fresh_seconds for result in results: - assert isinstance(result, Result) # Ignore results that are too far in the past if result.time < oldest_allowed: continue # Calculate freshness as the remaining time until this result # is no longer valid freshness = result.time - oldest_allowed - if isinstance(result, ResultError): - # Reduce the freshness for results containing errors so - # that they are not de-prioritized as much. This way, we - # will come back to them sooner to try again. - assert result.freshness_reduction_factor >= 0.0 - assert result.freshness_reduction_factor <= 1.0 - log.debug('Cutting freshness for a %s result by %d%% for ' - '%s', result.type.value, - result.freshness_reduction_factor * 100, - relay.nickname) + if isinstance(result, ResultError) \ + and prioritize_result_error is True: + # log.debug('Cutting freshness for a %s result by %d%% for' + # ' %s', result.type.value, + # result.freshness_reduction_factor * 100, + # relay.nickname) + # result.freshness_reduction_factor are hard-coded values + # on how much prioritize measurements that failed + # depending on the type of error. + # In a future refactor, create these values on an algorithm + # or create constants. freshness *= max(1.0-result.freshness_reduction_factor, 0) priority += freshness + # In a future refactor, do not create a new attribute relay.priority = priority # Sort the relays by their priority, with the smallest (best) priority # relays at the front relays = sorted(relays, key=lambda r: r.priority) - cutoff = max(int(len(relays) * self.fraction_to_return), - self.min_to_return) + fn_tstop = Decimal(time.time()) fn_tdelta = (fn_tstop - fn_tstart) * 1000 log.info('Spent %f msecs calculating relay best priority', fn_tdelta) - # Finally, slowly return the relays to the caller (after removing the - # priority member we polluted the variable with ...) - for relay in relays[0:cutoff]: + + # Return a fraction of relays in the network if return_fraction is + # True, otherwise return all. + cutoff = max(int(len(relays) * self.fraction_to_return), + self.min_to_return) + upper_limit = cutoff if return_fraction else len(relays) + # NOTE: these two are blocking, write to disk + # Increment the number of times ``best_priority`` has been run. + self.increment_priority_lists() + # Increment the number of relays that have been "prioritized". + # Because in a small testing network the upper limit could be smaller + # than the number of relays in the network, use the length of the list. + self.increment_priority_relays(len(relays[0:upper_limit])) + for relay in relays[0:upper_limit]: log.debug('Returning next relay %s with priority %f', relay.nickname, relay.priority) + # In a future refactor, a new attribute should not be created, + # then no need to remove it. del(relay.priority) + # Increment the number of times a realy was "prioritized" to be + # measured. + relay.increment_relay_recent_priority_list_count() yield relay diff -Nru sbws-1.0.2/sbws/lib/resultdump.py sbws-1.1.0/sbws/lib/resultdump.py --- sbws-1.0.2/sbws/lib/resultdump.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/lib/resultdump.py 2019-03-29 13:47:26.000000000 +0000 @@ -4,7 +4,6 @@ import logging from glob import glob from threading import Thread -from threading import Event from threading import RLock from queue import Queue from queue import Empty @@ -14,6 +13,7 @@ from sbws.globals import RESULT_VERSION, fail_hard from sbws.util.filelock import DirectoryLock from sbws.lib.relaylist import Relay +from .. import settings log = logging.getLogger(__name__) @@ -133,6 +133,8 @@ Results as a list ''' assert isinstance(fresh_days, int) assert os.path.isdir(datadir) + # Inform the results are being loaded, since it takes some seconds. + log.info("Reading and processing previous measurements.") results = {} today = datetime.utcfromtimestamp(time.time()) data_period = fresh_days + 2 @@ -190,29 +192,82 @@ ErrorCircuit = 'error-circ' ErrorStream = 'error-stream' ErrorAuth = 'error-auth' + # When it can not be found a second relay suitable to measure a relay. + # It is used in ``ResultErrorSecondRelay``. + ErrorSecondRelay = 'error-second-relay' + # When there is not a working destination Web Server. + # It is used in ``ResultErrorDestionation``. + ErrorDestination = 'error-destination' class Result: - ''' A simple struct to pack a measurement result into so that other code - can be confident it is handling a well-formed result. ''' + """A bandwidth measurement for a relay. + + It re-implements :class:`~sbws.lib.relaylist.Relay` as a inner class. + """ class Relay: - ''' Implements just enough of a stem RouterStatusEntryV3 for this - Result class to be happy ''' + """A Tor relay. + + It re-implements :class:`~sbws.lib.relaylist.Relay` + with the attributes needed. + + .. note:: in a future refactor it would be simpler if a ``Relay`` has + measurements and a measurement has a relay, + instead of every measurement re-implementing ``Relay``. + """ def __init__(self, fingerprint, nickname, address, master_key_ed25519, - average_bandwidth=None, observed_bandwidth=None): + average_bandwidth=None, burst_bandwidth=None, + observed_bandwidth=None, consensus_bandwidth=None, + consensus_bandwidth_is_unmeasured=None, + # Counters to be stored by relay and not per measurement, + # since the measurements might fail. + relay_in_recent_consensus_count=None, + relay_recent_measurement_attempt_count=None, + relay_recent_priority_list_count=None): + """ + Initializes a ``Result.Relay``. + + .. note:: in a future refactor the attributes should be dinamic + to easy adding/removing them. + They are shared by :class:`~sbws.lib.relaylist.Relay` and + :class:`~sbws.lib.v3bwfile.V3BWLine` and there should not be + repeated in every class. + """ self.fingerprint = fingerprint self.nickname = nickname self.address = address self.master_key_ed25519 = master_key_ed25519 self.average_bandwidth = average_bandwidth + self.burst_bandwidth = burst_bandwidth self.observed_bandwidth = observed_bandwidth - - def __init__(self, relay, circ, dest_url, scanner_nick, t=None): - self._relay = Result.Relay(relay.fingerprint, relay.nickname, - relay.address, relay.master_key_ed25519, - relay.average_bandwidth, - relay.observed_bandwidth) + self.consensus_bandwidth = consensus_bandwidth + self.consensus_bandwidth_is_unmeasured = \ + consensus_bandwidth_is_unmeasured + self.relay_in_recent_consensus_count = \ + relay_in_recent_consensus_count + self.relay_recent_measurement_attempt_count = \ + relay_recent_measurement_attempt_count + self.relay_recent_priority_list_count = \ + relay_recent_priority_list_count + + def __init__(self, relay, circ, dest_url, scanner_nick, t=None, + relay_in_recent_consensus_count=None): + """ + Initilizes the measurement and the relay with all the relay attributes. + """ + self._relay = Result.Relay( + relay.fingerprint, relay.nickname, + relay.address, relay.master_key_ed25519, + relay.average_bandwidth, + relay.burst_bandwidth, + relay.observed_bandwidth, + relay.consensus_bandwidth, + relay.consensus_bandwidth_is_unmeasured, + relay.relay_in_recent_consensus_count, + relay.relay_recent_measurement_attempt_count, + relay.relay_recent_priority_list_count + ) self._circ = circ self._dest_url = dest_url self._scanner = scanner_nick @@ -227,10 +282,22 @@ return self._relay.average_bandwidth @property + def relay_burst_bandwidth(self): + return self._relay.burst_bandwidth + + @property def relay_observed_bandwidth(self): return self._relay.observed_bandwidth @property + def consensus_bandwidth(self): + return self._relay.consensus_bandwidth + + @property + def consensus_bandwidth_is_unmeasured(self): + return self._relay.consensus_bandwidth_is_unmeasured + + @property def fingerprint(self): return self._relay.fingerprint @@ -247,6 +314,29 @@ return self._relay.master_key_ed25519 @property + def relay_in_recent_consensus_count(self): + """Number of times the relay was in a consensus.""" + return self._relay.relay_in_recent_consensus_count + + @property + def relay_recent_measurement_attempt_count(self): + """Returns the relay recent measurements attemps. + + It is initialized in :class:`~sbws.lib.relaylist.Relay` and + incremented in :func:`~sbws.core.scanner.main_loop`. + """ + return self._relay.relay_recent_measurement_attempt_count + + @property + def relay_recent_priority_list_count(self): + """Returns the relay recent "prioritization"s to be measured. + + It is initialized in :class:`~sbws.lib.relaylist.Relay` and + incremented in :func:`~sbws.core.scanner.main_loop`. + """ + return self._relay.relay_recent_priority_list_count + + @property def circ(self): return self._circ @@ -278,15 +368,31 @@ 'type': self.type, 'scanner': self.scanner, 'version': self.version, + 'relay_in_recent_consensus_count': + self.relay_in_recent_consensus_count, + 'relay_recent_measurement_attempt_count': + self.relay_recent_measurement_attempt_count, + 'relay_recent_priority_list_count': + self.relay_recent_priority_list_count, } @staticmethod def from_dict(d): - ''' Given a dict, returns the Result* subtype that is represented by - the dict. If we don't know how to parse the dict into a Result and it's - likely because the programmer forgot to implement something, raises - NotImplementedError. If we can't parse the dict for some other reason, - return None. ''' + """ + Returns a :class:`~sbws.lib.resultdump.Result` subclass from a + dictionary. + + Returns None if the ``version`` attribute is not + :const:`~sbws.globals.RESULT_VERSION` + + It raises ``NotImplementedError`` when the dictionary ``type`` can not + be parsed. + + .. note:: in a future refactor, the conversions to/from + object-dictionary will be simpler using ``setattr`` and ``__dict__`` + + ``version`` is not being used and should be removed. + """ assert 'version' in d if d['version'] != RESULT_VERSION: return None @@ -301,6 +407,10 @@ return ResultErrorStream.from_dict(d) elif d['type'] == _ResultType.ErrorAuth.value: return ResultErrorAuth.from_dict(d) + elif d['type'] == _ResultType.ErrorSecondRelay.value: + return ResultErrorSecondRelay.from_dict(d) + elif d['type'] == _ResultType.ErrorDestination.value: + return ResultErrorDestination.from_dict(d) else: raise NotImplementedError( 'Unknown result type {}'.format(d['type'])) @@ -345,7 +455,14 @@ return ResultError( Result.Relay( d['fingerprint'], d['nickname'], d['address'], - d['master_key_ed25519']), + d['master_key_ed25519'], + relay_in_recent_consensus_count= # noqa + d.get('relay_in_recent_consensus_count', None), # noqa + relay_recent_measurement_attempt_count= # noqa + d.get('relay_recent_measurement_attempt_count', None), # noqa + relay_recent_priority_list_count= # noqa + d.get('relay_recent_priority_list_count', None), # noqa + ), d['circ'], d['dest_url'], d['scanner'], msg=d['msg'], t=d['time']) @@ -386,7 +503,14 @@ return ResultErrorCircuit( Result.Relay( d['fingerprint'], d['nickname'], d['address'], - d['master_key_ed25519']), + d['master_key_ed25519'], + relay_in_recent_consensus_count= # noqa + d.get('relay_in_recent_consensus_count', None), # noqa + relay_recent_measurement_attempt_count= # noqa + d.get('relay_recent_measurement_attempt_count', None), # noqa + relay_recent_priority_list_count= # noqa + d.get('relay_recent_priority_list_count', None), # noqa + ), d['circ'], d['dest_url'], d['scanner'], msg=d['msg'], t=d['time']) @@ -409,6 +533,90 @@ return ResultErrorStream( Result.Relay( d['fingerprint'], d['nickname'], d['address'], + d['master_key_ed25519'], + relay_in_recent_consensus_count= # noqa + d.get('relay_in_recent_consensus_count', None), # noqa + relay_recent_measurement_attempt_count= # noqa + d.get('relay_recent_measurement_attempt_count', None), # noqa + relay_recent_priority_list_count= # noqa + d.get('relay_recent_priority_list_count', None), # noqa + ), + d['circ'], d['dest_url'], d['scanner'], + msg=d['msg'], t=d['time']) + + def to_dict(self): + d = super().to_dict() + return d + + +class ResultErrorSecondRelay(ResultError): + """ + Error when it could not be found a second relay suitable to measure + a relay. + + A second suitable relay is a relay that: + - Has at least equal bandwidth as the relay to measure. + - If the relay to measure is not an exit, + the second relay is an exit without `bad` flag and can exit to port 443. + - If the relay to measure is an exit, the second relay is not an exit. + + It is instanciated in :func:`~sbws.core.scanner.measure_relay`. + + .. note:: this duplicates code and add more tech-debt, + since it's the same as the other + :class:`~sbws.lib.resultdump.ResultError` classes except for the + ``type``. + In a future refactor, there should be only one ``ResultError`` class + and assign the type in the ``scanner`` module. + """ + def __init__(self, *a, **kw): + super().__init__(*a, **kw) + + @property + def type(self): + return _ResultType.ErrorSecondRelay + + @staticmethod + def from_dict(d): + assert isinstance(d, dict) + return ResultErrorSecondRelay( + Result.Relay( + d['fingerprint'], d['nickname'], d['address'], + d['master_key_ed25519']), + d['circ'], d['dest_url'], d['scanner'], + msg=d['msg'], t=d['time']) + + def to_dict(self): + d = super().to_dict() + return d + + +class ResultErrorDestination(ResultError): + """ + Error when there is not a working destination Web Server. + + It is instanciated in :func:`~sbws.core.scanner.measure_relay`. + + .. note:: this duplicates code and add more tech-debt, + since it's the same as the other + :class:`~sbws.lib.resultdump.ResultError` classes except for the + ``type``. + In a future refactor, there should be only one ``ResultError`` class + and assign the type in the ``scanner`` module. + """ + def __init__(self, *a, **kw): + super().__init__(*a, **kw) + + @property + def type(self): + return _ResultType.ErrorSecondRelay + + @staticmethod + def from_dict(d): + assert isinstance(d, dict) + return ResultErrorSecondRelay( + Result.Relay( + d['fingerprint'], d['nickname'], d['address'], d['master_key_ed25519']), d['circ'], d['dest_url'], d['scanner'], msg=d['msg'], t=d['time']) @@ -445,7 +653,14 @@ return ResultErrorAuth( Result.Relay( d['fingerprint'], d['nickname'], d['address'], - d['master_key_ed25519']), + d['master_key_ed25519'], + relay_in_recent_consensus_count= # noqa + d.get('relay_in_recent_consensus_count', None), # noqa + relay_recent_measurement_attempt_count= # noqa + d.get('relay_recent_measurement_attempt_count', None), # noqa + relay_recent_priority_list_count= # noqa + d.get('relay_recent_priority_list_count', None), # noqa + ), d['circ'], d['dest_url'], d['scanner'], msg=d['msg'], t=d['time']) @@ -480,7 +695,16 @@ Result.Relay( d['fingerprint'], d['nickname'], d['address'], d['master_key_ed25519'], d['relay_average_bandwidth'], - d['relay_observed_bandwidth']), + d.get('relay_burst_bandwidth'), d['relay_observed_bandwidth'], + d.get('consensus_bandwidth'), + d.get('consensus_bandwidth_is_unmeasured'), + relay_in_recent_consensus_count= # noqa + d.get('relay_in_recent_consensus_count', None), # noqa + relay_recent_measurement_attempt_count= # noqa + d.get('relay_recent_measurement_attempt_count', None), # noqa + relay_recent_priority_list_count= # noqa + d.get('relay_recent_priority_list_count', None), # noqa + ), d['circ'], d['dest_url'], d['scanner'], t=d['time']) @@ -490,7 +714,11 @@ 'rtts': self.rtts, 'downloads': self.downloads, 'relay_average_bandwidth': self.relay_average_bandwidth, + 'relay_burst_bandwidth': self.relay_burst_bandwidth, 'relay_observed_bandwidth': self.relay_observed_bandwidth, + 'consensus_bandwidth': self.consensus_bandwidth, + 'consensus_bandwidth_is_unmeasured': + self.consensus_bandwidth_is_unmeasured, }) return d @@ -498,13 +726,11 @@ class ResultDump: ''' Runs the enter() method in a new thread and collects new Results on its queue. Writes them to daily result files in the data directory ''' - def __init__(self, args, conf, end_event): + def __init__(self, args, conf): assert os.path.isdir(conf.getpath('paths', 'datadir')) - assert isinstance(end_event, Event) self.conf = conf self.fresh_days = conf.getint('general', 'data_period') self.datadir = conf.getpath('paths', 'datadir') - self.end_event = end_event self.data = {} self.data_lock = RLock() self.thread = Thread(target=self.enter) @@ -534,21 +760,54 @@ assert isinstance(result, Result) fp = result.fingerprint nick = result.nickname - if isinstance(result, ResultError) and self.end_event.is_set(): + if isinstance(result, ResultError) and settings.end_event.is_set(): log.debug('Ignoring %s for %s %s because we are shutting down', type(result).__name__, nick, fp) return self.store_result(result) write_result_to_datadir(result, self.datadir) - log.info('%s %s finished measurement with %s', nick, fp[0:8], - type(result).__name__) + if result.type == "success": + msg = "Success measuring {} ({}) via circuit {} and " \ + "destination {}".format( + result.fingerprint, result.nickname, result.circ, + result.dest_url) + else: + msg = "Error measuring {} ({}) via circuit {} and " \ + "destination {}: {}".format( + result.fingerprint, result.nickname, result.circ, + result.dest_url, result.msg) + # When the error is that there are not more functional destinations. + if result.type == "error-destination": + log.info("Shutting down because there are not functional " + "destinations.") + # NOTE: Because this is executed in a thread, stop_threads can not + # be call from here, it has to be call from the main thread. + # Instead set the singleton end event, that will call stop_threads + # from the main process. + settings.end_event.set() + log.info(msg) def enter(self): - ''' Main loop for the ResultDump thread ''' + """Main loop for the ResultDump thread. + + When there are results in the queue, queue.get will get them until + there are not anymore or timeout happen. + + For every result it gets, it process it and store in the filesystem, + which takes ~1 millisecond and will not trigger the timeout. + It can then store in the filesystem ~1000 results per second. + + I does not accept any other data type than Results or list of Results, + therefore is not possible to put big data types in the queue. + + If there are not any results in the queue, it waits 1 second and checks + again. + + """ with self.data_lock: self.data = load_recent_results_in_datadir( self.fresh_days, self.datadir) - while not (self.end_event.is_set() and self.queue.empty()): + while not (settings.end_event.is_set() and self.queue.empty()): try: event = self.queue.get(timeout=1) except Empty: diff -Nru sbws-1.0.2/sbws/lib/v3bwfile.py sbws-1.1.0/sbws/lib/v3bwfile.py --- sbws-1.0.2/sbws/lib/v3bwfile.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/lib/v3bwfile.py 2019-03-29 13:47:26.000000000 +0000 @@ -4,6 +4,7 @@ import copy import logging +import math import os from itertools import combinations from statistics import median, mean @@ -13,11 +14,11 @@ from sbws.globals import (SPEC_VERSION, BW_LINE_SIZE, SBWS_SCALE_CONSTANT, TORFLOW_SCALING, SBWS_SCALING, TORFLOW_BW_MARGIN, TORFLOW_OBS_LAST, TORFLOW_OBS_MEAN, - TORFLOW_ROUND_DIG, MIN_REPORT, MAX_BW_DIFF_PERC) + PROP276_ROUND_DIG, MIN_REPORT, MAX_BW_DIFF_PERC) from sbws.lib.resultdump import ResultSuccess, _ResultType from sbws.util.filelock import DirectoryLock from sbws.util.timestamp import (now_isodt_str, unixts_to_isodt_str, - now_unixts) + now_unixts, isostr_to_dt_obj) from sbws.util.state import State log = logging.getLogger(__name__) @@ -25,19 +26,90 @@ LINE_SEP = '\n' KEYVALUE_SEP_V1 = '=' KEYVALUE_SEP_V2 = ' ' + +# NOTE: in a future refactor make make all the KeyValues be a dictionary +# with their type, so that it's more similar to stem parser. + +# Header KeyValues +# ================= # List of the extra KeyValues accepted by the class EXTRA_ARG_KEYVALUES = ['software', 'software_version', 'file_created', - 'earliest_bandwidth', 'generator_started'] + 'earliest_bandwidth', 'generator_started', + 'scanner_country', 'destinations_countries'] +# number_eligible_relays is the number that ends in the bandwidth file +# ie, have not been excluded by one of the filters in 4. below +# They should be call recent_measurement_included_count to be congruent +# with the other KeyValues. STATS_KEYVALUES = ['number_eligible_relays', 'minimum_number_eligible_relays', 'number_consensus_relays', 'percent_eligible_relays', 'minimum_percent_eligible_relays'] -KEYVALUES_INT = STATS_KEYVALUES + +# KeyValues that count the number of relays that are in the bandwidth file, +# but ignored by Tor when voting, because they do not have a +# measured bandwidth. +BW_HEADER_KEYVALUES_RECENT_MEASUREMENTS_EXCLUDED = [ + # Number of relays that were measured but all the measurements failed + # because of network failures or it was + # not found a suitable helper relay + 'recent_measurements_excluded_error_count', + # Number of relays that have successful measurements but the measurements + # were not away from each other in X time (by default 1 day). + 'recent_measurements_excluded_near_count', + # Number of relays that have successful measurements and they are away from + # each other but they are not X time recent. + # By default this is 5 days, which is the same time the older + # the measurements can be by default. + 'recent_measurements_excluded_old_count', + # Number of relays that have successful measurements and they are away from + # each other and recent + # but the number of measurements are less than X (by default 2). + 'recent_measurements_excluded_few_count', +] +# Added in #29591 +# NOTE: recent_consensus_count, recent_priority_list_count, +# recent_measurement_attempt_count and recent_priority_relay_count +# are not reset when the scanner is stop. +# They will accumulate the values since the scanner was ever started. +BW_HEADER_KEYVALUES_MONITOR = [ + # 1.1 header: the number of different consensuses, that sbws has seen, + # since the last 5 days + 'recent_consensus_count', + # 2.4 Number of times a priority list has been created + 'recent_priority_list_count', + # 2.5 Number of relays that there were in a priority list + # [50, number of relays in the network * 0.05] + 'recent_priority_relay_count', + # 3.6 header: the number of times that sbws has tried to measure any relay, + # since the last 5 days + # This would be the number of times a relays were in a priority list + 'recent_measurement_attempt_count', + # 3.7 header: the number of times that sbws has tried to measure any relay, + # since the last 5 days, but it didn't work + # This should be the number of attempts - number of ResultSuccess - + # something else we don't know yet + # So far is the number of ResultError + 'recent_measurement_failure_count', + # The time it took to report about half of the network. + 'time_to_report_half_network', +] + BW_HEADER_KEYVALUES_RECENT_MEASUREMENTS_EXCLUDED +BANDWIDTH_HEADER_KEY_VALUES_INIT = \ + ['earliest_bandwidth', 'generator_started', + 'scanner_country', 'destinations_countries']\ + + STATS_KEYVALUES \ + + BW_HEADER_KEYVALUES_MONITOR + +KEYVALUES_INT = STATS_KEYVALUES + BW_HEADER_KEYVALUES_MONITOR # List of all unordered KeyValues currently being used to generate the file UNORDERED_KEYVALUES = EXTRA_ARG_KEYVALUES + STATS_KEYVALUES + \ - ['latest_bandwidth'] + ['latest_bandwidth'] + \ + BW_HEADER_KEYVALUES_MONITOR # List of all the KeyValues currently being used to generate the file ALL_KEYVALUES = ['version'] + UNORDERED_KEYVALUES + TERMINATOR = '=====' + +# Bandwidth Lines KeyValues +# ========================= # Num header lines in v1.X.X using all the KeyValues NUM_LINES_HEADER_V1 = len(ALL_KEYVALUES) + 2 LINE_TERMINATOR = TERMINATOR + LINE_SEP @@ -48,19 +120,107 @@ BW_KEYVALUES_BASIC = ['node_id', 'bw'] BW_KEYVALUES_FILE = BW_KEYVALUES_BASIC + \ ['master_key_ed25519', 'nick', 'rtt', 'time', - 'success', 'error_stream', 'error_circ', 'error_misc'] -BW_KEYVALUES_EXTRA_BWS = ['bw_median', 'bw_mean', 'desc_bw_avg', - 'desc_bw_obs_last', 'desc_bw_obs_mean'] -BW_KEYVALUES_EXTRA = BW_KEYVALUES_FILE + BW_KEYVALUES_EXTRA_BWS + 'success', 'error_stream', 'error_circ', 'error_misc', + # `vote=0` is used for the relays that were excluded to + # be reported in the bandwidth file and now they are + # reported. + # It tells Tor to do not vote on the relay. + # `unmeasured=1` is used for the same relays and it is + # added in case Tor would vote on them in future versions. + # Maybe these keys should not be included for the relays + # in which vote=1 and unmeasured=0. + 'vote', 'unmeasured', + # When there not enough eligible relays (not excluded) + # under_min_report is 1, `vote` is 0. + # Added in #29853. + 'under_min_report', + # Added in #292951 + 'error_second_relay', 'error_destination'] +BW_KEYVALUES_EXTRA_BWS = ['bw_median', 'bw_mean', 'desc_bw_avg', 'desc_bw_bur', + 'desc_bw_obs_last', 'desc_bw_obs_mean', + 'consensus_bandwidth', + 'consensus_bandwidth_is_unmeasured'] + +# Added in #292951 +BANDWIDTH_LINE_KEY_VALUES_MONITOR = [ + # 1.2 relay: the number of different consensuses, that sbws has seen, + # since the last 5 days, that have this relay + 'relay_in_recent_consensus_count', + # 2.6 relay: the number of times a relay was "prioritized" to be measured + # in the recent days (by default 5). + 'relay_recent_priority_list_count', + # 3.8 relay: the number of times that sbws has tried to measure + # this relay, since the last 5 days + # This would be the number of times a relay was in a priority list (2.6) + # since once it gets measured, it either returns ResultError, + # ResultSuccess or something else happened that we don't know yet + 'relay_recent_measurement_attempt_count', + # 3.9 relay: the number of times that sbws has tried to measure + # this relay, since the last 5 days, but it didn't work + # This should be the number of attempts - number of ResultSuccess - + # something else we don't know yet + # So far is the number of ResultError + 'relay_recent_measurement_failure_count', + # Number of error results created in the last 5 days that are excluded. + # This is the sum of all the errors. + 'relay_recent_measurements_excluded_error_count', + # The number of successful results, created in the last 5 days, + # that were excluded by a rule, for this relay. + # 'relay_recent_measurements_excluded_error_count' would be the + # sum of the following 3 + the number of error results. + + # The number of successful measurements that are not X time away + # from each other (by default 1 day). + 'relay_recent_measurements_excluded_near_count', + # The number of successful measurements that are away from each other + # but not X time recent (by default 5 days). + 'relay_recent_measurements_excluded_old_count', + # The number of measurements excluded because they are not at least X + # (by default 2). + 'relay_recent_measurements_excluded_few_count', +] +BW_KEYVALUES_EXTRA = BW_KEYVALUES_FILE + BW_KEYVALUES_EXTRA_BWS \ + + BANDWIDTH_LINE_KEY_VALUES_MONITOR +# NOTE: tech-debt: assign boolean type to vote and unmeasured, +# when the attributes are defined with a type, as stem does. BW_KEYVALUES_INT = ['bw', 'rtt', 'success', 'error_stream', - 'error_circ', 'error_misc'] + BW_KEYVALUES_EXTRA_BWS + 'error_circ', 'error_misc', 'vote', 'unmeasured', + 'under_min_report'] \ + + BW_KEYVALUES_EXTRA_BWS \ + + BANDWIDTH_LINE_KEY_VALUES_MONITOR BW_KEYVALUES = BW_KEYVALUES_BASIC + BW_KEYVALUES_EXTRA -def kb_round_x_sig_dig(bw_bs, digits=TORFLOW_ROUND_DIG): - """Convert bw to KB and round to x most significat digits.""" - bw_kb = bw_bs / 1000 - return max(int(round(bw_kb, -digits)), 1) +def round_sig_dig(n, digits=PROP276_ROUND_DIG): + """Round n to 'digits' significant digits in front of the decimal point. + Results less than or equal to 1 are rounded to 1. + Returns an integer. + + digits must be greater than 0. + n must be less than or equal to 2**73, to avoid floating point errors. + """ + digits = int(digits) + assert digits >= 1 + if n <= 1: + return 1 + digits_in_n = int(math.log10(n)) + 1 + round_digits = max(digits_in_n - digits, 0) + rounded_n = round(n, -round_digits) + return int(rounded_n) + + +def kb_round_x_sig_dig(bw_bs, digits=PROP276_ROUND_DIG): + """Convert bw_bs from bytes to kilobytes, and round the result to + 'digits' significant digits. + Results less than or equal to 1 are rounded up to 1. + Returns an integer. + + digits must be greater than 0. + n must be less than or equal to 2**82, to avoid floating point errors. + """ + # avoid double-rounding by using floating-point + bw_kb = bw_bs / 1000.0 + return round_sig_dig(bw_kb, digits=digits) def num_results_of_type(results, type_str): @@ -103,7 +263,7 @@ # same as timestamp self.latest_bandwidth = unixts_to_isodt_str(timestamp) [setattr(self, k, v) for k, v in kwargs.items() - if k in EXTRA_ARG_KEYVALUES] + if k in BANDWIDTH_HEADER_KEY_VALUES_INIT] def __str__(self): if self.version.startswith('1.'): @@ -111,16 +271,55 @@ return self.strv2 @classmethod - def from_results(cls, results, state_fpath=''): + def from_results(cls, results, scanner_country=None, + destinations_countries=None, state_fpath=''): kwargs = dict() latest_bandwidth = cls.latest_bandwidth_from_results(results) earliest_bandwidth = cls.earliest_bandwidth_from_results(results) + # NOTE: Blocking, reads file generator_started = cls.generator_started_from_file(state_fpath) + recent_consensus_count = cls.consensus_count_from_file(state_fpath) timestamp = str(latest_bandwidth) kwargs['latest_bandwidth'] = unixts_to_isodt_str(latest_bandwidth) kwargs['earliest_bandwidth'] = unixts_to_isodt_str(earliest_bandwidth) if generator_started is not None: kwargs['generator_started'] = generator_started + # To be compatible with older bandwidth files, do not require it. + if scanner_country is not None: + kwargs['scanner_country'] = scanner_country + if destinations_countries is not None: + kwargs['destinations_countries'] = destinations_countries + if recent_consensus_count is not None: + kwargs['recent_consensus_count'] = str(recent_consensus_count) + + recent_measurement_attempt_count = \ + cls.recent_measurement_attempt_count_from_file(state_fpath) + if recent_measurement_attempt_count is not None: + kwargs['recent_measurement_attempt_count'] = \ + str(recent_measurement_attempt_count) + + # If it is a failure that is not a ResultError, then + # failures = attempts - all mesaurements + # Works only in the case that old measurements files already had + # measurements count + if recent_measurement_attempt_count is not None: + all_measurements = 0 + for result_list in results.values(): + all_measurements += len(result_list) + measurement_failures = (recent_measurement_attempt_count + - all_measurements) + kwargs['recent_measurement_failure_count'] = \ + str(measurement_failures) + + priority_lists = cls.recent_priority_list_count_from_file(state_fpath) + if priority_lists is not None: + kwargs['recent_priority_list_count'] = str(priority_lists) + + priority_relays = \ + cls.recent_priority_relay_count_from_file(state_fpath) + if priority_relays is not None: + kwargs['recent_priority_relay_count'] = str(priority_relays) + h = cls(timestamp, **kwargs) return h @@ -178,6 +377,44 @@ return None @staticmethod + def consensus_count_from_file(state_fpath): + state = State(state_fpath) + if 'recent_consensus_count' in state: + return state['recent_consensus_count'] + else: + return None + + # NOTE: in future refactor store state in the class + @staticmethod + def recent_measurement_attempt_count_from_file(state_fpath): + """ + Returns the number of times any relay was queued to be measured + in the recent (by default 5) days from the state file. + """ + state = State(state_fpath) + return state.get('recent_measurement_attempt_count', None) + + @staticmethod + def recent_priority_list_count_from_file(state_fpath): + """ + Returns the number of times + :meth:`~sbws.lib.relayprioritizer.RelayPrioritizer.best_priority` + was run + in the recent (by default 5) days from the state file. + """ + state = State(state_fpath) + return state.get('recent_priority_list_count', None) + + @staticmethod + def recent_priority_relay_count_from_file(state_fpath): + """ + Returns the number of times any relay was "prioritized" to be measured + in the recent (by default 5) days from the state file. + """ + state = State(state_fpath) + return state.get('recent_priority_relay_count', None) + + @staticmethod def latest_bandwidth_from_results(results): return round(max([r.time for fp in results for r in results[fp]])) @@ -235,27 +472,83 @@ [setattr(self, k, str(v)) for k, v in kwargs.items() if k in STATS_KEYVALUES] + def add_time_report_half_network(self): + """Add to the header the time it took to measure half of the network. + + It is not the time the scanner actually takes on measuring all the + network, but the ``number_eligible_relays`` that are reported in the + bandwidth file and directory authorities will vote on. + + This is calculated for half of the network, so that failed or not + reported relays do not affect too much. + + For instance, if there are 6500 relays in the network, half of the + network would be 3250. And if there were 4000 eligible relays + measured in an interval of 3 days, the time to measure half of the + network would be 3 days * 3250 / 4000. + + Since the elapsed time is calculated from the earliest and the + latest measurement and a relay might have more than 2 measurements, + this would give an estimate on how long it would take to measure + the network including all the valid measurements. + + Log also an estimated on how long it would take with the current + number of relays included in the bandwidth file. + """ + # NOTE: in future refactor do not convert attributes to str until + # writing to the file, so that they do not need to be converted back + # to do some calculations. + elapsed_time = ( + (isostr_to_dt_obj(self.latest_bandwidth) + - isostr_to_dt_obj(self.earliest_bandwidth)) + .total_seconds()) + + # This attributes were added later and some tests that + # do not initialize them would fail. + eligible_relays = int(getattr(self, 'number_eligible_relays', 0)) + consensus_relays = int(getattr(self, 'number_consensus_relays', 0)) + if not(eligible_relays and consensus_relays): + return + + half_network = consensus_relays / 2 + # Calculate the time it would take to measure half of the network + if eligible_relays >= half_network: + time_half_network = round( + elapsed_time * half_network / eligible_relays + ) + self.time_to_report_half_network = str(time_half_network) + + # In any case log an estimated on the time to measure all the network. + estimated_time = round( + elapsed_time * consensus_relays / eligible_relays + ) + log.info("Estimated time to measure the network: %s hours.", + round(estimated_time / 60 / 60)) + + def add_relays_excluded_counters(self, exclusion_dict): + """ + Add the monitoring KeyValues to the header about the number of + relays not included because they were not ``eligible``. + """ + log.debug("Adding relays excluded counters.") + for k, v in exclusion_dict.items(): + setattr(self, k, str(v)) + class V3BWLine(object): """ Create a Bandwidth List line following the spec version 1.X.X. - :param str node_id: - :param int bw: - :param dict kwargs: extra headers. Currently supported: + :param str node_id: the relay fingerprint + :param int bw: the bandwidth value that directory authorities will include + in their votes. + :param dict kwargs: extra headers. - - nickname, str - - master_key_ed25519, str - - rtt, int - - time, str - - sucess, int - - error_stream, int - - error_circ, int - - error_misc, int + .. note:: tech-debt: move node_id and bw to kwargs and just ensure that + the required values are in **kwargs """ def __init__(self, node_id, bw, **kwargs): assert isinstance(node_id, str) - assert isinstance(bw, int) assert node_id.startswith('$') self.node_id = node_id self.bw = bw @@ -277,7 +570,6 @@ divided by the the time it took to received. bw = data (Bytes) / time (seconds) """ - success_results = [r for r in results if isinstance(r, ResultSuccess)] # log.debug("Len success_results %s", len(success_results)) node_id = '$' + results[0].fingerprint kwargs = dict() @@ -286,36 +578,126 @@ kwargs['master_key_ed25519'] = results[0].master_key_ed25519 kwargs['time'] = cls.last_time_from_results(results) kwargs.update(cls.result_types_from_results(results)) - # useful args for scaling - if success_results: - if not len(success_results) >= min_num: - # log.debug('The number of results is les than %s', min_num) - return None - results_away = \ - cls.results_away_each_other(success_results, secs_away) - if not results_away: - return None - # log.debug("Results away from each other: %s", - # [unixts_to_isodt_str(r.time) for r in results_away]) - results_recent = cls.results_recent_than(results_away, secs_recent) - if not results_recent: - return None - kwargs['desc_bw_avg'] = \ - results_recent[-1].relay_average_bandwidth - rtt = cls.rtt_from_results(results_recent) - if rtt: - kwargs['rtt'] = rtt - bw = cls.bw_median_from_results(results_recent) - kwargs['bw_mean'] = cls.bw_mean_from_results(results_recent) - kwargs['bw_median'] = cls.bw_median_from_results( + consensuses_count = \ + [r.relay_in_recent_consensus_count for r in results + if getattr(r, 'relay_in_recent_consensus_count', None)] + if consensuses_count: + consensus_count = max(consensuses_count) + kwargs['relay_in_recent_consensus_count'] = str(consensus_count) + + measurements_attempts = \ + [r.relay_recent_measurement_attempt_count for r in results + if getattr(r, 'relay_recent_measurement_attempt_count', None)] + if measurements_attempts: + kwargs['relay_recent_measurement_attempt_count'] = \ + str(max(measurements_attempts)) + + relay_recent_priority_list_counts = \ + [r.relay_recent_priority_list_count for r in results + if getattr(r, 'relay_recent_priority_list_count', None)] + if relay_recent_priority_list_counts: + kwargs['relay_recent_priority_list_count'] = \ + str(max(relay_recent_priority_list_counts)) + + success_results = [r for r in results if isinstance(r, ResultSuccess)] + + # NOTE: The following 4 conditions exclude relays from the bandwidth + # file when the measurements does not satisfy some rules, what makes + # the relay non-`eligible`. + # In BANDWIDTH_LINE_KEY_VALUES_MONITOR it is explained what they mean. + # In BW_HEADER_KEYVALUES_RECENT_MEASUREMENTS_EXCLUDED it is also + # explained the what it means the strings returned. + # They rules were introduced in #28061 and #27338 + # In #28565 we introduce the KeyValues to know why they're excluded. + # In #28563 we report these relays, but make Tor ignore them. + # This might confirm #28042. + + # If the relay is non-`eligible`: + # Create a bandwidth line with the relay, but set ``vote=0`` so that + # Tor versions with patch #29806 does not vote on the relay. + # Set ``bw=1`` so that Tor versions without the patch, + # will give the relay low bandwidth. + # Include ``unmeasured=1`` in case Tor would vote on unmeasured relays + # in future versions. + # And return because there are not bandwidth values. + # NOTE: the bandwidth values could still be obtained if: + # 1. ``ResultError`` will store them + # 2. assign ``results_recent = results`` when there is a ``exclusion + # reason. + # This could be done in a better way as part of a refactor #28684. + + kwargs['vote'] = 0 + kwargs['unmeasured'] = 1 + + exclusion_reason = None + + number_excluded_error = len(results) - len(success_results) + if number_excluded_error > 0: + # then the number of error results is the number of results + kwargs['relay_recent_measurements_excluded_error_count'] = \ + number_excluded_error + if not success_results: + exclusion_reason = 'recent_measurements_excluded_error_count' + return (cls(node_id, 1, **kwargs), exclusion_reason) + + results_away = \ + cls.results_away_each_other(success_results, secs_away) + number_excluded_near = len(success_results) - len(results_away) + if number_excluded_near > 0: + kwargs['relay_recent_measurements_excluded_near_count'] = \ + number_excluded_near + if not results_away: + exclusion_reason = \ + 'recent_measurements_excluded_near_count' + return (cls(node_id, 1, **kwargs), exclusion_reason) + # log.debug("Results away from each other: %s", + # [unixts_to_isodt_str(r.time) for r in results_away]) + + results_recent = cls.results_recent_than(results_away, secs_recent) + number_excluded_old = len(results_away) - len(results_recent) + if number_excluded_old > 0: + kwargs['relay_recent_measurements_excluded_old_count'] = \ + number_excluded_old + if not results_recent: + exclusion_reason = \ + 'recent_measurements_excluded_old_count' + return (cls(node_id, 1, **kwargs), exclusion_reason) + + if not len(results_recent) >= min_num: + kwargs['relay_recent_measurements_excluded_few_count'] = \ + len(results_recent) + # log.debug('The number of results is less than %s', min_num) + exclusion_reason = \ + 'recent_measurements_excluded_few_count' + return (cls(node_id, 1, **kwargs), exclusion_reason) + + # For any line not excluded, do not include vote and unmeasured + # KeyValues + del kwargs['vote'] + del kwargs['unmeasured'] + + rtt = cls.rtt_from_results(results_recent) + if rtt: + kwargs['rtt'] = rtt + bw = cls.bw_median_from_results(results_recent) + kwargs['bw_mean'] = cls.bw_mean_from_results(results_recent) + kwargs['bw_median'] = cls.bw_median_from_results( + results_recent) + kwargs['desc_bw_avg'] = \ + cls.desc_bw_avg_from_results(results_recent) + kwargs['desc_bw_bur'] = \ + cls.desc_bw_bur_from_results(results_recent) + kwargs['consensus_bandwidth'] = \ + cls.consensus_bandwidth_from_results(results_recent) + kwargs['consensus_bandwidth_is_unmeasured'] = \ + cls.consensus_bandwidth_is_unmeasured_from_results( results_recent) - kwargs['desc_bw_obs_last'] = \ - cls.desc_bw_obs_last_from_results(results_recent) - kwargs['desc_bw_obs_mean'] = \ - cls.desc_bw_obs_mean_from_results(results_recent) - bwl = cls(node_id, bw, **kwargs) - return bwl - return None + kwargs['desc_bw_obs_last'] = \ + cls.desc_bw_obs_last_from_results(results_recent) + kwargs['desc_bw_obs_mean'] = \ + cls.desc_bw_obs_mean_from_results(results_recent) + bwl = cls(node_id, bw, **kwargs) + return bwl, None @classmethod def from_data(cls, data, fingerprint): @@ -349,7 +731,7 @@ return results # log.debug("Results are NOT away from each other in at least %ss: %s", # secs_away, [unixts_to_isodt_str(r.time) for r in results]) - return None + return [] @staticmethod def results_recent_than(results, secs_recent=None): @@ -393,6 +775,38 @@ return rt_dict @staticmethod + def desc_bw_avg_from_results(results): + """Obtain the last descriptor bandwidth average from the results.""" + for r in reversed(results): + if r.relay_average_bandwidth is not None: + return r.relay_average_bandwidth + return None + + @staticmethod + def desc_bw_bur_from_results(results): + """Obtain the last descriptor bandwidth burst from the results.""" + for r in reversed(results): + if r.relay_burst_bandwidth is not None: + return r.relay_burst_bandwidth + return None + + @staticmethod + def consensus_bandwidth_from_results(results): + """Obtain the last consensus bandwidth from the results.""" + for r in reversed(results): + if r.consensus_bandwidth is not None: + return r.consensus_bandwidth + return None + + @staticmethod + def consensus_bandwidth_is_unmeasured_from_results(results): + """Obtain the last consensus unmeasured flag from the results.""" + for r in reversed(results): + if r.consensus_bandwidth_is_unmeasured is not None: + return r.consensus_bandwidth_is_unmeasured + return None + + @staticmethod def desc_bw_obs_mean_from_results(results): desc_bw_obs_ls = [] for r in results: @@ -456,11 +870,13 @@ for bw_line in self.bw_lines]) @classmethod - def from_results(cls, results, state_fpath='', + def from_results(cls, results, scanner_country=None, + destinations_countries=None, state_fpath='', scale_constant=SBWS_SCALE_CONSTANT, - scaling_method=None, torflow_obs=TORFLOW_OBS_LAST, + scaling_method=TORFLOW_SCALING, + torflow_obs=TORFLOW_OBS_LAST, torflow_cap=TORFLOW_BW_MARGIN, - torflow_round_digs=TORFLOW_ROUND_DIG, + round_digs=PROP276_ROUND_DIG, secs_recent=None, secs_away=None, min_num=0, consensus_path=None, max_bw_diff_perc=MAX_BW_DIFF_PERC, reverse=False): @@ -470,7 +886,7 @@ :param str state_fpath: path to the state file :param int scaling_method: Scaling method to obtain the bandwidth - Posiable values: {NONE, SBWS_SCALING, TORFLOW_SCALING} = {0, 1, 2} + Possible values: {None, SBWS_SCALING, TORFLOW_SCALING} = {0, 1, 2} :param int scale_constant: sbws scaling constant :param int torflow_obs: method to choose descriptor observed bandwidth :param bool reverse: whether to sort the bw lines descending or not @@ -481,42 +897,74 @@ 'relay_fp2': [Result1, Result2, ...]} """ - # TODO: change scaling_method to TORFLOW_SCALING before getting this - # in production log.info('Processing results to generate a bandwidth list file.') - header = V3BWHeader.from_results(results, state_fpath) + header = V3BWHeader.from_results(results, scanner_country, + destinations_countries, state_fpath) bw_lines_raw = [] + bw_lines_excluded = [] number_consensus_relays = cls.read_number_consensus_relays( consensus_path) state = State(state_fpath) + + # Create a dictionary with the number of relays excluded by any of the + # of the filtering rules that makes relays non-`eligible`. + # NOTE: In BW_HEADER_KEYVALUES_RECENT_MEASUREMENTS_EXCLUDED it is + # explained what are the KeyValues. + # See also the comments in `from_results`. + exclusion_dict = dict( + [(k, 0) for k in BW_HEADER_KEYVALUES_RECENT_MEASUREMENTS_EXCLUDED] + ) for fp, values in results.items(): # log.debug("Relay fp %s", fp) - line = V3BWLine.from_results(values, secs_recent, secs_away, - min_num) - if line is not None: + line, reason = V3BWLine.from_results(values, secs_recent, + secs_away, min_num) + # If there is no reason it means the line will not be excluded. + if not reason: bw_lines_raw.append(line) + else: + # Store the excluded lines to include them in the bandwidth + # file. + bw_lines_excluded.append(line) + exclusion_dict[reason] = exclusion_dict.get(reason, 0) + 1 + # Add the headers with the number of excluded relays by reason + header.add_relays_excluded_counters(exclusion_dict) + if not bw_lines_raw: + # It could be possible to scale the lines that were successful + # even if excluded, but is not done here. log.info("After applying restrictions to the raw results, " "there is not any. Scaling can not be applied.") + # Update the header and log the progress. cls.update_progress( - cls, bw_lines_raw, header, number_consensus_relays, state) - return cls(header, []) + cls, 0, header, number_consensus_relays, state) + # Set the lines that would be excluded anyway (`vote=0`) with + # `under_min_report=1` + cls.set_under_min_report(bw_lines_excluded) + # Create the bandwidth file with the lines that would be excluded. + return cls(header, bw_lines_excluded) if scaling_method == SBWS_SCALING: bw_lines = cls.bw_sbws_scale(bw_lines_raw, scale_constant) cls.warn_if_not_accurate_enough(bw_lines, scale_constant) # log.debug(bw_lines[-1]) elif scaling_method == TORFLOW_SCALING: bw_lines = cls.bw_torflow_scale(bw_lines_raw, torflow_obs, - torflow_cap, torflow_round_digs) + torflow_cap, round_digs) # log.debug(bw_lines[-1]) - cls.update_progress( - cls, bw_lines, header, number_consensus_relays, state) + # Update the header and log the progress. + min_perc = cls.update_progress( + cls, len(bw_lines), header, number_consensus_relays, state + ) + # If after scaling the number of lines is less than the percentage + # of lines to report, set them with `under_min_report`. + if not min_perc: + cls.set_under_min_report(bw_lines) else: bw_lines = cls.bw_kb(bw_lines_raw) # log.debug(bw_lines[-1]) # Not using the result for now, just warning cls.is_max_bw_diff_perc_reached(bw_lines, max_bw_diff_perc) - f = cls(header, bw_lines) + header.add_time_report_half_network() + f = cls(header, bw_lines + bw_lines_excluded) return f @classmethod @@ -541,6 +989,17 @@ return cls(header, bw_lines) @staticmethod + def set_under_min_report(bw_lines): + """ + Mondify the Bandwidth Lines adding the KeyValue `under_min_report`, + `vote`. + """ + log.debug("Setting `under_min_report` to %s lines.", len(bw_lines)) + for l in bw_lines: + l.under_min_report = 1 + l.vote = 0 + + @staticmethod def bw_kb(bw_lines, reverse=False): bw_lines_scaled = copy.deepcopy(bw_lines) for l in bw_lines_scaled: @@ -561,17 +1020,6 @@ the bandwidth to obtain the new bandwidth :returns list: V3BwLine list """ - # If a relay has MaxAdvertisedBandwidth set, they may be capable of - # some large amount of bandwidth but prefer if they didn't receive it. - # We also could have managed to measure them faster than their - # {,Relay}BandwidthRate somehow. - # - # See https://github.com/pastly/simple-bw-scanner/issues/155 and - # https://trac.torproject.org/projects/tor/ticket/8494 - # - # Note how this isn't some measured-by-us average of bandwidth. It's - # the first value on the 'bandwidth' line in the relay's server - # descriptor. log.debug('Scaling bandwidth using sbws method.') m = median([l.bw for l in bw_lines]) bw_lines_scaled = copy.deepcopy(bw_lines) @@ -597,13 +1045,25 @@ @staticmethod def is_max_bw_diff_perc_reached(bw_lines, max_bw_diff_perc=MAX_BW_DIFF_PERC): - sum_consensus_bw = sum([l.desc_bw_obs_last for l in bw_lines]) - sum_bw = sum([l.bw for l in bw_lines]) - diff = min(sum_consensus_bw, sum_bw) / max(sum_consensus_bw, sum_bw) - diff_perc = diff * 100 - log.info("The difference between the total consensus bandwidth " - "and the total measured bandwidth is %s%% percent", - diff_perc) + # Since old versions were not storing consensus bandwidth, use getattr. + sum_consensus_bw = sum([l.consensus_bandwidth for l in bw_lines + if getattr(l, 'consensus_bandwidth', None)]) + # Because the scaled bandwidth is in KB, but not the stored consensus + # bandwidth, multiply by 1000. + # Do not count 1 bandwidths for the relays that were excluded + # and exclude also the bw of the relays that did not stored consensus, + # since they are not included either in the sum of the consensus. + sum_bw = sum([l.bw for l in bw_lines + if getattr(l, 'consensus_bandwidth', None) + and getattr(l, 'unmeasured', 0) == 0]) * 1000 + # Percentage difference + diff_perc = ( + abs(sum_consensus_bw - sum_bw) + / ((sum_consensus_bw + sum_bw) / 2) + ) * 100 + log.info("The difference between the total consensus bandwidth (%s)" + "and the total measured bandwidth (%s) is %s%%.", + sum_consensus_bw, sum_bw, round(diff_perc)) if diff_perc > MAX_BW_DIFF_PERC: log.warning("It is more than %s%%", max_bw_diff_perc) return True @@ -612,7 +1072,7 @@ @staticmethod def bw_torflow_scale(bw_lines, desc_bw_obs_type=TORFLOW_OBS_MEAN, cap=TORFLOW_BW_MARGIN, - num_round_dig=TORFLOW_ROUND_DIG, reverse=False): + num_round_dig=PROP276_ROUND_DIG, reverse=False): """ Obtain final bandwidth measurements applying Torflow's scaling method. @@ -699,12 +1159,15 @@ **desc_bw**: - It is the ``observed bandwidth`` in the descriptor, NOT the ``average - bandwidth``:: + It is the minimum of all the descriptor bandwidth values:: + + bws = map(int, g) + bw_observed = min(bws) return Router(ns.idhex, ns.nickname, bw_observed, dead, exitpolicy, ns.flags, ip, version, os, uptime, published, contact, rate_limited, # NOQA ns.orhash, ns.bandwidth, extra_info_digest, ns.unmeasured) + self.desc_bw = max(bw,1) # Avoid div by 0 **new_bw**:: @@ -744,19 +1207,19 @@ \\sum_{i=1}^{n}bwnew_i \\times 0.05\\right) \\ &= min\\left( - \\left(bwobs_i \\times r_i\\right), - \\sum_{i=1}^{n}\\left(bwobs_i \\times r_i\\right) + \\left(min\\left(bwobs_i, bwavg_i, bwbur_i \\right) \\times r_i\\right), + \\sum_{i=1}^{n}\\left(min\\left(bwobs_i, bwavg_i, bwbur_i \\right) \\times r_i\\right) \\times 0.05\\right)\\ &= min\\left( - \\left(bwobs_i \\times max\\left(rf_i, rs_i\\right)\\right), - \\sum_{i=1}^{n}\\left(bwobs_i \\times + \\left(min\\left(bwobs_i, bwavg_i, bwbur_i \\right) \\times max\\left(rf_i, rs_i\\right)\\right), + \\sum_{i=1}^{n}\\left(min\\left(bwobs_i, bwavg_i, bwbur_i \\right) \\times max\\left(rf_i, rs_i\\right)\\right) \\times 0.05\\right)\\ &= min\\left( - \\left(bwobs_i \\times max\\left(\\frac{bwfilt_i}{bwfilt}, + \\left(min\\left(bwobs_i, bwavg_i, bwbur_i \\right) \\times max\\left(\\frac{bwfilt_i}{bwfilt}, \\frac{bw_i}{bwstrm}\\right)\\right), - \\sum_{i=1}^{n}\\left(bwobs_i \\times + \\sum_{i=1}^{n}\\left(min\\left(bwobs_i, bwavg_i, bwbur_i \\right) \\times max\\left(\\frac{bwfilt_i}{bwfilt}, \\frac{bw_i}{bwstrm}\\right)\\right) \\times 0.05\\right) @@ -776,22 +1239,54 @@ log.debug('muf %s', muf) log.debug('hlimit %s', hlimit) for l in bw_lines_tf: + # Because earlier versions did not store this values, check first + # they exists. Do not report any error, since they will be stored + if not(l.desc_bw_obs_last or l.desc_bw_obs_mean and l.desc_bw_avg): + log.debug("Skipping %s from scaling, because there were not " + "descriptor bandwidths.", l.nick) + continue if desc_bw_obs_type == TORFLOW_OBS_LAST: desc_bw_obs = l.desc_bw_obs_last elif desc_bw_obs_type == TORFLOW_OBS_MEAN: desc_bw_obs = l.desc_bw_obs_mean - # just applying the formula above: - bw_new = kb_round_x_sig_dig( - max( - l.bw_mean / mu, # ratio - max(l.bw_mean, mu) / muf # ratio filtered - ) * desc_bw_obs, \ - digits=num_round_dig) # convert to KB + # Excerpt from bandwidth-file-spec.txt section 2.3 + # A relay's MaxAdvertisedBandwidth limits the bandwidth-avg in its + # descriptor. + # Therefore generators MUST limit a relay's measured bandwidth to + # its descriptor's bandwidth-avg. + # Generators SHOULD NOT limit measured bandwidths based on + # descriptors' bandwidth-observed, because that penalises new + # relays. + # See https://trac.torproject.org/projects/tor/ticket/8494 + if l.desc_bw_bur is not None: + # Because in previous versions results were not storing + # desc_bw_bur + desc_bw = min(desc_bw_obs, l.desc_bw_bur, l.desc_bw_avg) + else: + desc_bw = min(desc_bw_obs, l.desc_bw_avg) + # In previous versions results were not storing consensus_bandwidth + if l.consensus_bandwidth_is_unmeasured \ + or l.consensus_bandwidth is None: + min_bandwidth = desc_bw + # If the relay is measured, use the minimum between the descriptors + # bandwidth and the consensus bandwidth, so that + # MaxAdvertisedBandwidth limits the consensus weight + # The consensus bandwidth in a measured relay has been obtained + # doing the same calculation as here + else: + min_bandwidth = min(desc_bw, l.consensus_bandwidth) + # Torflow's scaling + ratio_stream = l.bw_mean / mu + ratio_stream_filtered = max(l.bw_mean, mu) / muf + ratio = max(ratio_stream, ratio_stream_filtered) + bw_scaled = ratio * min_bandwidth + # round and convert to KB + bw_new = kb_round_x_sig_dig(bw_scaled, digits=num_round_dig) # Cap maximum bw if cap is not None: bw_new = min(hlimit, bw_new) - # remove decimals and avoid 0 - l.bw = max(round(bw_new), 1) + # avoid 0 + l.bw = max(bw_new, 1) return sorted(bw_lines_tf, key=lambda x: x.bw, reverse=reverse) @staticmethod @@ -809,7 +1304,7 @@ return num @staticmethod - def measured_progress_stats(bw_lines, number_consensus_relays, + def measured_progress_stats(num_bw_lines, number_consensus_relays, min_perc_reached_before): """ Statistics about measurements progress, to be included in the header. @@ -827,14 +1322,14 @@ # It will not be updated to the last consensus, but the list of # measured relays is not either. assert isinstance(number_consensus_relays, int) - assert isinstance(bw_lines, list) + assert isinstance(num_bw_lines, int) statsd = {} - statsd['number_eligible_relays'] = len(bw_lines) + statsd['number_eligible_relays'] = num_bw_lines statsd['number_consensus_relays'] = number_consensus_relays statsd['minimum_number_eligible_relays'] = round( statsd['number_consensus_relays'] * MIN_REPORT / 100) statsd['percent_eligible_relays'] = round( - len(bw_lines) * 100 / statsd['number_consensus_relays']) + num_bw_lines * 100 / statsd['number_consensus_relays']) statsd['minimum_percent_eligible_relays'] = MIN_REPORT if statsd['number_eligible_relays'] < \ statsd['minimum_number_eligible_relays']: @@ -860,7 +1355,7 @@ @property def sum_bw(self): - return sum([l.bw for l in self.bw_lines]) + return sum([l.bw for l in self.bw_lines if hasattr(l, 'bw')]) @property def num(self): @@ -868,19 +1363,19 @@ @property def mean_bw(self): - return mean([l.bw for l in self.bw_lines]) + return mean([l.bw for l in self.bw_lines if hasattr(l, 'bw')]) @property def median_bw(self): - return median([l.bw for l in self.bw_lines]) + return median([l.bw for l in self.bw_lines if hasattr(l, 'bw')]) @property def max_bw(self): - return max([l.bw for l in self.bw_lines]) + return max([l.bw for l in self.bw_lines if hasattr(l, 'bw')]) @property def min_bw(self): - return min([l.bw for l in self.bw_lines]) + return min([l.bw for l in self.bw_lines if hasattr(l, 'bw')]) @property def info_stats(self): @@ -890,20 +1385,27 @@ ['sum_bw', 'mean_bw', 'median_bw', 'num', 'max_bw', 'min_bw']] - def update_progress(self, bw_lines, header, number_consensus_relays, + def update_progress(self, num_bw_lines, header, number_consensus_relays, state): + """ + Returns True if the minimim percent of Bandwidth Lines was reached + and False otherwise. + Update the header with the progress. + """ min_perc_reached_before = state.get('min_perc_reached') if number_consensus_relays is not None: statsd, success = self.measured_progress_stats( - bw_lines, number_consensus_relays, min_perc_reached_before) + num_bw_lines, number_consensus_relays, min_perc_reached_before) # add statistics about progress always header.add_stats(**statsd) if not success: - bw_lines = [] + # From sbws 1.1.0 the lines are reported (#29853) even if they + # are less than the minimum percent. state['min_perc_reached'] = None + return False else: state['min_perc_reached'] = now_isodt_str() - return bw_lines + return True def bw_line_for_node_id(self, node_id): """Returns the bandwidth line for a given node fingerprint. diff -Nru sbws-1.0.2/sbws/util/config.py sbws-1.1.0/sbws/util/config.py --- sbws-1.0.2/sbws/util/config.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/util/config.py 2019-03-29 13:47:26.000000000 +0000 @@ -10,7 +10,9 @@ from tempfile import NamedTemporaryFile from sbws.globals import (DEFAULT_CONFIG_PATH, DEFAULT_LOG_CONFIG_PATH, USER_CONFIG_PATH, SUPERVISED_RUN_DPATH, - SUPERVISED_USER_CONFIG_PATH, fail_hard) + SUPERVISED_USER_CONFIG_PATH) + +from sbws.util.iso3166 import ISO_3166_ALPHA_2 _ALPHANUM = 'abcdefghijklmnopqrstuvwxyz' _ALPHANUM += _ALPHANUM.upper() @@ -66,12 +68,18 @@ assert isinstance(conf, ConfigParser) if args.config: if not os.path.isfile(args.config): - fail_hard('Configuration file %s not found.', args.config) + # XXX: The logger is not configured at this stage, + # sbws should start with a logger before reading configurations. + print('Configuration file %s not found, using defaults.' % + args.config) + return conf + print('Using configuration provided as argument %s' % args.config) return _extend_config(conf, args.config) user_config_path = _obtain_user_conf_path() if os.path.isfile(user_config_path): + print('Using configuration file %s' % user_config_path) return _extend_config(conf, user_config_path) - log.debug('No user config found.') + log.debug('No user config found, using defaults.') return conf @@ -117,8 +125,6 @@ def configure_logging(args, conf): assert isinstance(conf, ConfigParser) logger = 'logger_sbws' - if args.log_level: - conf['logging']['level'] = args.log_level.upper() # Set the correct handler(s) based on [logging] options handlers = set() can_log_to_file, reason = _can_log_to_file(conf) @@ -162,6 +168,15 @@ conf['logging']['to_stdout_level'].upper() conf['handler_to_syslog']['level'] = \ conf['logging']['to_syslog_level'].upper() + # If there's a log_level cli argument, the user would expect that level + # in the standard output. + # conf['logging']['level'] sets the lower level, but it's still needed to + # set the stdout level. + # It also must be set up in the end, since cli arguments have higher + # priority. + if args.log_level: + conf['logging']['level'] = args.log_level.upper() + conf['handler_to_stdout']['level'] = conf['logging']['level'] # Now we configure the standard python logging system with NamedTemporaryFile('w+t') as fd: conf.write(fd) @@ -256,6 +271,21 @@ return errors +def _validate_country(conf, sec, key, err_tmpl): + errors = [] + if conf[sec].get(key, None) is None: + errors.append(err_tmpl.substitute( + sec=sec, key=key, val=None, + e="Missing country in configuration file.")) + return errors + valid = conf[sec]['country'] in ISO_3166_ALPHA_2 + if not valid: + errors.append(err_tmpl.substitute( + sec=sec, key=key, val=conf[sec][key], + e="Not a valid ISO 3166 alpha-2 country code.")) + return errors + + def _validate_scanner(conf): errors = [] sec = 'scanner' @@ -275,7 +305,7 @@ 'download_max': {'minimum': 0.001, 'maximum': None}, } all_valid_keys = list(ints.keys()) + list(floats.keys()) + \ - ['nickname'] + ['nickname', 'country'] errors.extend(_validate_section_keys(conf, sec, all_valid_keys, err_tmpl)) errors.extend(_validate_section_ints(conf, sec, ints, err_tmpl)) errors.extend(_validate_section_floats(conf, sec, floats, err_tmpl)) @@ -283,6 +313,7 @@ if not valid: errors.append(err_tmpl.substitute( sec=sec, key='nickname', val=conf[sec]['nickname'], e=error_msg)) + errors.extend(_validate_country(conf, sec, 'country', err_tmpl)) return errors @@ -375,15 +406,18 @@ urls = { 'url': {}, } - all_valid_keys = list(urls.keys()) + ['verify'] + all_valid_keys = list(urls.keys()) \ + + ['verify', 'country', 'max_num_failures'] for sec in dest_sections: if sec not in conf: errors.append('{} is an enabled destination but is not a ' 'section in the config'.format(sec)) continue errors.extend(_validate_section_keys( - conf, sec, all_valid_keys, err_tmpl, allow_missing=['verify'])) + conf, sec, all_valid_keys, err_tmpl, + allow_missing=['verify', 'max_num_failures'])) errors.extend(_validate_section_urls(conf, sec, urls, err_tmpl)) + errors.extend(_validate_country(conf, sec, 'country', err_tmpl)) return errors @@ -513,12 +547,16 @@ def _validate_url(section, key): value = section[key] - if not value.startswith(('http://', 'https://')): - return False, 'Must start with http:// or https://' url = urlparse(value) - assert url.scheme in ['http', 'https'] if not url.netloc: return False, 'Does not appear to contain a hostname' + # It should be possible to have an URL that starts by http:// that uses + # TLS,but python requests is just checking the scheme starts by https + # when verifying certificate: + # https://github.com/requests/requests/blob/master/requests/adapters.py#L215 # noqa + # When the scheme is https but the protocol is not TLS, requests will hang. + if url.scheme != 'https' and not url.netloc.startswith('127.0.0.1'): + return False, 'URL scheme must be HTTPS (except for the test server)' return True, '' diff -Nru sbws-1.0.2/sbws/util/iso3166.py sbws-1.1.0/sbws/util/iso3166.py --- sbws-1.0.2/sbws/util/iso3166.py 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/sbws/util/iso3166.py 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,37 @@ +""" +ISO 3166 alpha-2 countries' codes. +Obtained from https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes. +Last updated 2019/02/05. +ZZ is not the code of any country and it is used to denote any country, +when the destination Web Server is in a CDN. +""" +# When the destination Web Server is in a CDN, the IP could be resolved by +# the exit relay and obtain the country from the IP. + +# It would be better to use some standard location system for geopgraphic areas +# that doesn't depend on political borders. +# It should be possible to obtain IP address location in that system too. + +ISO_3166_ALPHA_2 = [ + 'AF', 'AX', 'AL', 'DZ', 'AS', 'AD', 'AO', 'AI', 'AQ', 'AG', 'AR', 'AM', + 'AW', 'AU', 'AT', 'AZ', 'BS', 'BH', 'BD', 'BB', 'BY', 'BE', 'BZ', 'BJ', + 'BM', 'BT', 'BO', 'BQ', 'BA', 'BW', 'BV', 'BR', 'IO', 'BN', 'BG', 'BF', + 'BI', 'CV', 'KH', 'CM', 'CA', 'KY', 'CF', 'TD', 'CL', 'CN', 'CX', 'CC', + 'CO', 'KM', 'CD', 'CG', 'CK', 'CR', 'CI', 'HR', 'CU', 'CW', 'CY', 'CZ', + 'DK', 'DJ', 'DM', 'DO', 'EC', 'EG', 'SV', 'GQ', 'ER', 'EE', 'SZ', 'ET', + 'FK', 'FO', 'FJ', 'FI', 'FR', 'GF', 'PF', 'TF', 'GA', 'GM', 'GE', 'DE', + 'GH', 'GI', 'GR', 'GL', 'GD', 'GP', 'GU', 'GT', 'GG', 'GN', 'GW', 'GY', + 'HT', 'HM', 'VA', 'HN', 'HK', 'HU', 'IS', 'IN', 'ID', 'IR', 'IQ', 'IE', + 'IM', 'IL', 'IT', 'JM', 'JP', 'JE', 'JO', 'KZ', 'KE', 'KI', 'KP', 'KR', + 'KW', 'KG', 'LA', 'LV', 'LB', 'LS', 'LR', 'LY', 'LI', 'LT', 'LU', 'MO', + 'MK', 'MG', 'MW', 'MY', 'MV', 'ML', 'MT', 'MH', 'MQ', 'MR', 'MU', 'YT', + 'MX', 'FM', 'MD', 'MC', 'MN', 'ME', 'MS', 'MA', 'MZ', 'MM', 'NA', 'NR', + 'NP', 'NL', 'NC', 'NZ', 'NI', 'NE', 'NG', 'NU', 'NF', 'MP', 'NO', 'OM', + 'PK', 'PW', 'PS', 'PA', 'PG', 'PY', 'PE', 'PH', 'PN', 'PL', 'PT', 'PR', + 'QA', 'RE', 'RO', 'RU', 'RW', 'BL', 'SH', 'KN', 'LC', 'MF', 'PM', 'VC', + 'WS', 'SM', 'ST', 'SA', 'SN', 'RS', 'SC', 'SL', 'SG', 'SX', 'SK', 'SI', + 'SB', 'SO', 'ZA', 'GS', 'SS', 'ES', 'LK', 'SD', 'SR', 'SJ', 'SE', 'CH', + 'SY', 'TW', 'TJ', 'TZ', 'TH', 'TL', 'TG', 'TK', 'TO', 'TT', 'TN', 'TR', + 'TM', 'TC', 'TV', 'UG', 'UA', 'AE', 'GB', 'UM', 'US', 'UY', 'UZ', 'VU', + 'VE', 'VN', 'VG', 'VI', 'WF', 'EH', 'YE', 'ZM', 'ZW', 'ZZ' + ] diff -Nru sbws-1.0.2/sbws/util/requests.py sbws-1.1.0/sbws/util/requests.py --- sbws-1.0.2/sbws/util/requests.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/util/requests.py 2019-03-29 13:47:26.000000000 +0000 @@ -1,21 +1,37 @@ import requests -import sbws.util.stem as stem_utils + +from sbws import settings +from sbws.util import stem as stem_utils + + +class TimedSession(requests.Session): + """Requests Session that sends timeout in the head and get methods. + """ + + def get(self, url, **kwargs): + return super().get(url, timeout=getattr(self, "_timeout", None), + **kwargs) + + def head(self, url, **kwargs): + return super().head(url, timeout=getattr(self, "_timeout", None), + **kwargs) def make_session(controller, timeout): - s = requests.Session() + """ + Initialize a TimedSession with the timeout, the proxies and the headers. + + """ + s = TimedSession() socks_info = stem_utils.get_socks_info(controller) - s.sbws_proxies = { + # Probably because scanner is stopping. + if socks_info is None: + return None + s.proxies = { 'http': 'socks5h://{}:{}'.format(*socks_info), 'https': 'socks5h://{}:{}'.format(*socks_info), } - s.sbws_timeout = timeout + # ``_timeout`` is not used by request's Session, but it is by TimedSession. + s._timeout = timeout + s.headers = settings.HTTP_HEADERS return s - - -def get(s, url, **kw): - return s.get(url, timeout=s.sbws_timeout, proxies=s.sbws_proxies, **kw) - - -def head(s, url, **kw): - return s.head(url, timeout=s.sbws_timeout, proxies=s.sbws_proxies, **kw) diff -Nru sbws-1.0.2/sbws/util/state.py sbws-1.1.0/sbws/util/state.py --- sbws-1.0.2/sbws/util/state.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/util/state.py 2019-03-29 13:47:26.000000000 +0000 @@ -63,12 +63,16 @@ self._state = self._read() return self._state.__len__() - def get(self, key): + def get(self, key, d=None): + """ + Implements a dictionary ``get`` method reading and locking + a json file. + """ if not isinstance(key, str): raise TypeError( 'Keys must be strings. %s is a %s' % (key, type(key))) self._state = self._read() - return self._state.get(key) + return self._state.get(key, d) def __getitem__(self, key): if not isinstance(key, str): @@ -93,7 +97,6 @@ raise TypeError( 'May only store value with type in %s, not %s' % (State._ALLOWED_TYPES, type(value))) - self._state = self._read() self._state.__setitem__(key, value) self._write() diff -Nru sbws-1.0.2/sbws/util/stem.py sbws-1.1.0/sbws/util/stem.py --- sbws-1.0.2/sbws/util/stem.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/util/stem.py 2019-03-29 13:47:26.000000000 +0000 @@ -1,7 +1,9 @@ +import socks + from stem.control import (Controller, Listener) from stem import (SocketError, InvalidRequest, UnsatisfiableRequest, OperationFailed, ControllerError, InvalidArguments, - ProtocolError) + ProtocolError, SocketClosed) from stem.connection import IncorrectSocketType import stem.process from configparser import ConfigParser @@ -10,7 +12,9 @@ import logging import os from sbws.globals import fail_hard -from sbws.globals import TORRC_STARTING_POINT +from sbws.globals import (TORRC_STARTING_POINT, TORRC_RUNTIME_OPTIONS, + TORRC_OPTIONS_CAN_FAIL) +from sbws import settings log = logging.getLogger(__name__) stream_building_lock = RLock() @@ -47,6 +51,11 @@ def remove_event_listener(controller, func): try: controller.remove_event_listener(func) + except SocketClosed as e: + if not settings.end_event.is_set(): + log.debug(e) + else: + log.exception(e) except ProtocolError as e: log.exception("Exception trying to remove event %s", e) @@ -72,8 +81,12 @@ return None, 'Unable to reach tor on control socket' assert c is not None if set_custom_stream_settings: - c.set_conf('__DisablePredictedCircuits', '1') - c.set_conf('__LeaveStreamsUnattached', '1') + # These options are also set in launch_tor. + # In a future refactor they could be set in the case they are not + # already in the running instance. This way the controller_port + # could also be used. + set_torrc_options_can_fail(c) + set_torrc_runtime_options(c) return c, '' @@ -117,6 +130,78 @@ return c +def parse_user_torrc_config(torrc, torrc_text): + """Parse the user configuration torrc text call `extra_lines` + to a dictionary suitable to use with stem and return a new torrc + dictionary that merges that dictionary with the existing torrc. + Example: + [tor] + extra_lines = + Log debug file /tmp/tor-debug.log + NumCPUs 1 + """ + torrc_dict = torrc.copy() + for line in torrc_text.split('\n'): + # Remove leading and trailing whitespace, if any + line = line.strip() + # Ignore blank lines + if len(line) < 1: + continue + # Some torrc options are only a key, some are a key value pair. + kv = line.split(None, 1) + if len(kv) > 1: + key, value = kv + else: + key = kv[0] + value = None + # It's really easy to add to the torrc if the key doesn't exist + if key not in torrc: + torrc_dict.update({key: value}) + # But if it does, we have to make a list of values. For example, say + # the user wants to add a SocksPort and we already have + # 'SocksPort auto' in the torrc. We'll go from + # torrc['SocksPort'] == 'auto' + # to + # torrc['SocksPort'] == ['auto', '9050'] + else: + existing_val = torrc[key] + if isinstance(existing_val, str): + torrc_dict.update({key: [existing_val, value]}) + else: + assert isinstance(existing_val, list) + existing_val.append(value) + torrc_dict.update({key: existing_val}) + log.debug('Adding "%s %s" to torrc with which we are launching Tor', + key, value) + return torrc_dict + + +def set_torrc_runtime_options(controller): + """Set torrc options at runtime.""" + try: + controller.set_options(TORRC_RUNTIME_OPTIONS) + # Only the first option that fails will be logged here. + # Just log stem's exceptions. + except (ControllerError, InvalidRequest, InvalidArguments) as e: + log.exception(e) + exit(1) + + +def set_torrc_options_can_fail(controller): + """Set options that can fail, at runtime. + + They can be set at launch, but since the may fail because they are not + supported in some Tor versions, it's easier to try one by one at runtime + and ignore the ones that fail. + """ + for k, v in TORRC_OPTIONS_CAN_FAIL.items(): + try: + controller.set_conf(k, v) + except InvalidArguments as error: + log.debug('Ignoring option not supported by this Tor version. %s', + error) + + def launch_tor(conf): assert isinstance(conf, ConfigParser) os.makedirs(conf.getpath('tor', 'datadir'), mode=0o700, exist_ok=True) @@ -125,6 +210,7 @@ # Bare minimum things, more or less torrc = copy.deepcopy(TORRC_STARTING_POINT) # Very important and/or common settings that we don't know until runtime + # The rest of the settings are in globals.py torrc.update({ 'DataDirectory': conf.getpath('tor', 'datadir'), 'PidFile': conf.getpath('tor', 'pid'), @@ -139,53 +225,8 @@ 'LearnCircuitBuildTimeout': '0', 'CircuitBuildTimeout': conf['general']['circuit_timeout'], }) - # This block of code reads additional torrc lines from the user's - # config.ini so they can add arbitrary additional options. - # - # The user can't replace our options, only add to them. For example, - # there's no way to remove 'SocksPort auto' (if it is still in - # TORRC_STARTING_POINT). If you add a SocksPort in your config.ini, you'll - # open two socks ports. - # - # As an example, maybe the user hates their HDD and wants to fill it with - # debug logs, and wants to tell Tor to use only 1 CPU core. - # - # [tor] - # extra_lines = - # Log debug file /tmp/tor-debug.log - # NumCPUs 1 - for line in conf['tor']['extra_lines'].split('\n'): - # Remove leading and trailing whitespace, if any - line = line.strip() - # Ignore blank lines - if len(line) < 1: - continue - # The way stem handles configuring Tor with a dictionary is the first - # word is a key and the remaining words are the value. - kv = line.split(None, 1) - if len(kv) < 2: - fail_hard('All torrc lines must have 2 or more words. "%s" has ' - 'fewer', line) - key, value = kv - log.info('Adding "%s %s" to torrc with which we are launching Tor', - key, value) - # It's really easy to add to the torrc if the key doesn't exist - if key not in torrc: - torrc.update({key: value}) - # But if it does, we have to make a list of values. For example, say - # the user wants to add a SocksPort and we already have - # 'SocksPort auto' in the torrc. We'll go from - # torrc['SocksPort'] == 'auto' - # to - # torrc['SocksPort'] == ['auto', '9050'] - else: - existing_val = torrc[key] - if isinstance(existing_val, str): - torrc.update({key: [existing_val, value]}) - else: - assert isinstance(existing_val, list) - existing_val.append(value) - torrc.update({key: existing_val}) + + torrc = parse_user_torrc_config(torrc, conf['tor']['extra_lines']) # Finally launch Tor try: stem.process.launch_tor_with_config( @@ -194,15 +235,11 @@ fail_hard('Error trying to launch tor: %s', e) # And return a controller to it cont = _init_controller_socket(conf.getpath('tor', 'control_socket')) - # Because we build things by hand and can't set these before Tor bootstraps - try: - cont.set_conf('__DisablePredictedCircuits', '1') - cont.set_conf('__LeaveStreamsUnattached', '1') - except (ControllerError, InvalidArguments, InvalidRequest) as e: - log.exception("Error trying to launch tor: %s. " - "Maybe the tor directory is being used by other " - "sbws instance?", e) - exit(1) + # Set options that can fail at runtime + set_torrc_options_can_fail(cont) + # Set runtime options + set_torrc_runtime_options(cont) + log.info('Started and connected to Tor %s via %s', cont.get_version(), conf.getpath('tor', 'control_socket')) return cont @@ -214,9 +251,13 @@ try: socks_ports = controller.get_listeners(Listener.SOCKS) return socks_ports[0] + except SocketClosed as e: + if not settings.end_event.is_set(): + log.debug(e) + # This might need to return the eception if this happen in other cases + # than when stopping the scanner. except ControllerError as e: - log.exception("Exception trying to get socks info: %e.", e) - exit(1) + log.debug(e) def only_relays_with_bandwidth(controller, relays, min_bw=None, max_bw=None): @@ -230,10 +271,10 @@ assert max_bw is None or max_bw >= 0 ret = [] for relay in relays: - assert hasattr(relay, 'bandwidth') - if min_bw is not None and relay.bandwidth < min_bw: + assert hasattr(relay, 'consensus_bandwidth') + if min_bw is not None and relay.consensus_bandwidth < min_bw: continue - if max_bw is not None and relay.bandwidth > max_bw: + if max_bw is not None and relay.consensus_bandwidth > max_bw: continue ret.append(relay) return ret @@ -248,8 +289,9 @@ log.warning('Circuit %s no longer seems to exist so can\'t return ' 'a valid circuit string for it: %s', circ_id, e) return None - except ControllerError as e: - log.exception("Exception trying to get circuit string %s", e) + # exceptions raised when stopping the scanner + except (ControllerError, SocketClosed, socks.GeneralProxyError) as e: + log.debug(e) return None return '[' +\ ' -> '.join(['{} ({})'.format(n, fp[0:8]) for fp, n in circ.path]) +\ diff -Nru sbws-1.0.2/sbws/util/timestamp.py sbws-1.1.0/sbws/util/timestamp.py --- sbws-1.0.2/sbws/util/timestamp.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/sbws/util/timestamp.py 2019-03-29 13:47:26.000000000 +0000 @@ -14,6 +14,10 @@ return dt.replace(microsecond=0).isoformat() +def isostr_to_dt_obj(isostr): + return datetime.strptime(isostr, "%Y-%m-%dT%H:%M:%S") + + def unixts_to_dt_obj(unixts): """ Convert unix timestamp to naive datetime object in UTC time zone. diff -Nru sbws-1.0.2/tests/data/.sbws/datadir/2019-03-25.txt sbws-1.1.0/tests/data/.sbws/datadir/2019-03-25.txt --- sbws-1.0.2/tests/data/.sbws/datadir/2019-03-25.txt 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/data/.sbws/datadir/2019-03-25.txt 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +{"msg": "Could not connect to http://127.0.0.1:28888/sbws.bin over circ 6 [relay7 (E894C659) -> exit2 (C0606B41)]: SOCKSHTTPConnectionPool(host='127.0.0.1', port=28888): Read timed out. (read timeout=10.0)", "relay_recent_priority_list_count": 1, "time": 1553518998.6577215, "relay_in_recent_consensus_count": 1, "fingerprint": "C0606B414423F9A2BBA2679B440056E3B07FEC85", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "address": "127.10.0.12", "nickname": "exit2", "version": 4, "type": "error-stream", "master_key_ed25519": "iriD8sIKS25WGc6mLesQ2okT1Tcn81AuqnEbItJeuvY", "circ": ["E894C65997F8EC96558B554176EEEA39C6A43EF6", "C0606B414423F9A2BBA2679B440056E3B07FEC85"], "relay_recent_measurement_attempt_count": 1} +{"msg": "Could not connect to http://127.0.0.1:28888/sbws.bin over circ 5 [relay1 (2ABFBACE) -> exit1 (270A861A)]: SOCKSHTTPConnectionPool(host='127.0.0.1', port=28888): Read timed out. (read timeout=10.0)", "relay_recent_priority_list_count": 1, "time": 1553519008.7184815, "relay_in_recent_consensus_count": 1, "fingerprint": "2ABFBACE61167A1019A56CB35B2E3362B97D8570", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "address": "127.10.0.4", "nickname": "relay1", "version": 4, "type": "error-stream", "master_key_ed25519": "zOZX+yyD7EN1W/Y2wgjuvObpFOOWK+LZIWlKW0AOcIE", "circ": ["2ABFBACE61167A1019A56CB35B2E3362B97D8570", "270A861ABED22EC2B625198BCCD7B2B9DBFFC93C"], "relay_recent_measurement_attempt_count": 1} +{"msg": "Could not connect to http://127.0.0.1:28888/sbws.bin over circ 7 [relay5 (32B7178F) -> exit1 (270A861A)]: SOCKSHTTPConnectionPool(host='127.0.0.1', port=28888): Read timed out. (read timeout=10.0)", "relay_recent_priority_list_count": 1, "time": 1553519018.8218904, "relay_in_recent_consensus_count": 1, "fingerprint": "32B7178F7201F76411A99D3552F340D3597D5629", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "address": "127.10.0.8", "nickname": "relay5", "version": 4, "type": "error-stream", "master_key_ed25519": "5O+uCpFoBzsey33+Zzlgyy18/McmV1mpPJZ+DrZMKRc", "circ": ["32B7178F7201F76411A99D3552F340D3597D5629", "270A861ABED22EC2B625198BCCD7B2B9DBFFC93C"], "relay_recent_measurement_attempt_count": 1} +{"relay_recent_priority_list_count": 1, "consensus_bandwidth_is_unmeasured": false, "time": 1553519021.7934368, "relay_in_recent_consensus_count": 1, "fingerprint": "117A456C911114076BEB4E757AC48B16CC0CCC5F", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "consensus_bandwidth": 0, "relay_average_bandwidth": 1048576, "rtts": [], "address": "127.10.0.14", "nickname": "relay1mbyteMAB", "downloads": [{"amount": 33851243, "duration": 6.1733644008636475}, {"amount": 33851243, "duration": 6.220228672027588}, {"amount": 33851243, "duration": 5.932555913925171}, {"amount": 33851243, "duration": 6.066358804702759}, {"amount": 33851243, "duration": 6.243254899978638}], "version": 4, "type": "success", "relay_observed_bandwidth": 0, "relay_burst_bandwidth": 1073741824, "master_key_ed25519": "2fXiF4T993i+vVwZZEQ4fYee+N8OThzCGeacvnVaNbo", "circ": ["117A456C911114076BEB4E757AC48B16CC0CCC5F", "270A861ABED22EC2B625198BCCD7B2B9DBFFC93C"], "relay_recent_measurement_attempt_count": 1} +{"msg": "Could not connect to http://127.0.0.1:28888/sbws.bin over circ 10 [auth3 (35E3B8BB) -> exit1 (270A861A)]: SOCKSHTTPConnectionPool(host='127.0.0.1', port=28888): Read timed out. (read timeout=10.0)", "relay_recent_priority_list_count": 1, "time": 1553519031.9330049, "relay_in_recent_consensus_count": 1, "fingerprint": "35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "address": "127.10.0.3", "nickname": "auth3", "version": 4, "type": "error-stream", "master_key_ed25519": "r29RWlFMIdU5GUvKsWGdhQIKjIpUzqgw0yNx/7IpPFM", "circ": ["35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C", "270A861ABED22EC2B625198BCCD7B2B9DBFFC93C"], "relay_recent_measurement_attempt_count": 1} +{"msg": "Could not connect to http://127.0.0.1:28888/sbws.bin over circ 11 [relay4 (4D664E24) -> exit2 (C0606B41)]: SOCKSHTTPConnectionPool(host='127.0.0.1', port=28888): Read timed out. (read timeout=10.0)", "relay_recent_priority_list_count": 1, "time": 1553519042.0144875, "relay_in_recent_consensus_count": 1, "fingerprint": "4D664E247E530CA5CD5176B8C1A6DABC9531F0B0", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "address": "127.10.0.7", "nickname": "relay4", "version": 4, "type": "error-stream", "master_key_ed25519": "NQspfAK/xkywFHV5LsQ5lLi2BmTKx2Imu4jacJ0d9os", "circ": ["4D664E247E530CA5CD5176B8C1A6DABC9531F0B0", "C0606B414423F9A2BBA2679B440056E3B07FEC85"], "relay_recent_measurement_attempt_count": 1} +{"msg": "Could not connect to http://127.0.0.1:28888/sbws.bin over circ 13 [auth1 (AA45C130) -> exit3 (FC264325)]: SOCKSHTTPConnectionPool(host='127.0.0.1', port=28888): Read timed out. (read timeout=10.0)", "relay_recent_priority_list_count": 1, "time": 1553519052.0695426, "relay_in_recent_consensus_count": 1, "fingerprint": "AA45C13025C037F056E734169891878ED0880231", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "address": "127.10.0.1", "nickname": "auth1", "version": 4, "type": "error-stream", "master_key_ed25519": "wLglSEw9/DHfpNrlrqjVRSnGLVWfnm0vYxkryH4aT6Q", "circ": ["AA45C13025C037F056E734169891878ED0880231", "FC264325EA99D597FF94DA88379DABB64304DD9D"], "relay_recent_measurement_attempt_count": 1} +{"relay_recent_priority_list_count": 1, "consensus_bandwidth_is_unmeasured": false, "time": 1553519052.2012622, "relay_in_recent_consensus_count": 1, "fingerprint": "693F73187624BE760AAD2A12C5ED89DB1DE044F5", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "consensus_bandwidth": 0, "relay_average_bandwidth": 1073741824, "rtts": [], "address": "127.10.0.6", "nickname": "relay3", "downloads": [{"amount": 31324083, "duration": 5.3419859409332275}, {"amount": 31324083, "duration": 5.510152101516724}, {"amount": 31324083, "duration": 5.591760635375977}, {"amount": 31324083, "duration": 5.645946979522705}, {"amount": 31324083, "duration": 5.567358016967773}], "version": 4, "type": "success", "relay_observed_bandwidth": 0, "relay_burst_bandwidth": 1073741824, "master_key_ed25519": "/PrTbpen3BrKNgiNRhAa93JQtnrT3LJX3Ka1+jvbWj4", "circ": ["693F73187624BE760AAD2A12C5ED89DB1DE044F5", "270A861ABED22EC2B625198BCCD7B2B9DBFFC93C"], "relay_recent_measurement_attempt_count": 1} +{"msg": "Could not connect to http://127.0.0.1:28888/sbws.bin over circ 16 [auth2 (E7B3C9A0) -> exit2 (C0606B41)]: SOCKSHTTPConnectionPool(host='127.0.0.1', port=28888): Read timed out. (read timeout=10.0)", "relay_recent_priority_list_count": 1, "time": 1553519062.3665206, "relay_in_recent_consensus_count": 1, "fingerprint": "E7B3C9A0040D628DAC88B0251AE6334D28E8F531", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "address": "127.10.0.2", "nickname": "auth2", "version": 4, "type": "error-stream", "master_key_ed25519": "uPz8ZZNm2Gcra7BauJP5PH+7uANRraYpCj7NFtp1KdM", "circ": ["E7B3C9A0040D628DAC88B0251AE6334D28E8F531", "C0606B414423F9A2BBA2679B440056E3B07FEC85"], "relay_recent_measurement_attempt_count": 1} +{"msg": "Could not connect to http://127.0.0.1:28888/sbws.bin over circ 17 [relay1mbyteRBR (934E06F3) -> exit1 (270A861A)]: SOCKSHTTPConnectionPool(host='127.0.0.1', port=28888): Read timed out. (read timeout=10.0)", "relay_recent_priority_list_count": 1, "time": 1553519072.496785, "relay_in_recent_consensus_count": 1, "fingerprint": "934E06F38A391CB71DF83ECDE05DFF5CDE3AC49D", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "address": "127.10.0.15", "nickname": "relay1mbyteRBR", "version": 4, "type": "error-stream", "master_key_ed25519": "eR0HnYlzpOEGwxuFjZkGJZ6pu2eV1i6fd9lhF4UOMno", "circ": ["934E06F38A391CB71DF83ECDE05DFF5CDE3AC49D", "270A861ABED22EC2B625198BCCD7B2B9DBFFC93C"], "relay_recent_measurement_attempt_count": 1} +{"msg": "Could not connect to http://127.0.0.1:28888/sbws.bin over circ 20 [relay1mbyteRBR (934E06F3) -> exit3 (FC264325)]: SOCKSHTTPConnectionPool(host='127.0.0.1', port=28888): Read timed out. (read timeout=10.0)", "relay_recent_priority_list_count": 1, "time": 1553519082.5644238, "relay_in_recent_consensus_count": 1, "fingerprint": "FC264325EA99D597FF94DA88379DABB64304DD9D", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "address": "127.10.0.13", "nickname": "exit3", "version": 4, "type": "error-stream", "master_key_ed25519": "Wiv9uOemlcFzRY1gZfBOpbQ+aJn6NG0Z/IArZFCdSdk", "circ": ["934E06F38A391CB71DF83ECDE05DFF5CDE3AC49D", "FC264325EA99D597FF94DA88379DABB64304DD9D"], "relay_recent_measurement_attempt_count": 1} +{"relay_recent_priority_list_count": 1, "consensus_bandwidth_is_unmeasured": false, "time": 1553519086.9947243, "relay_in_recent_consensus_count": 1, "fingerprint": "270A861ABED22EC2B625198BCCD7B2B9DBFFC93C", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "consensus_bandwidth": 0, "relay_average_bandwidth": 1073741824, "rtts": [], "address": "127.10.0.11", "nickname": "exit1", "downloads": [{"amount": 35501959, "duration": 6.359417676925659}, {"amount": 35501959, "duration": 6.289767503738403}, {"amount": 35501959, "duration": 6.6014111042022705}, {"amount": 35501959, "duration": 6.559557914733887}, {"amount": 35501959, "duration": 6.426588773727417}], "version": 4, "type": "success", "relay_observed_bandwidth": 0, "relay_burst_bandwidth": 1073741824, "master_key_ed25519": "C506YEdasDDQqidu4G2VLMFOwaqMX28BYkuxr1+wI9o", "circ": ["E7B3C9A0040D628DAC88B0251AE6334D28E8F531", "270A861ABED22EC2B625198BCCD7B2B9DBFFC93C"], "relay_recent_measurement_attempt_count": 1} +{"msg": "Could not connect to http://127.0.0.1:28888/sbws.bin over circ 23 [relay2 (8E687E91) -> exit2 (C0606B41)]: SOCKSHTTPConnectionPool(host='127.0.0.1', port=28888): Read timed out. (read timeout=10.0)", "relay_recent_priority_list_count": 1, "time": 1553519097.167445, "relay_in_recent_consensus_count": 1, "fingerprint": "8E687E91DCAB967F6E4EE8E46E66F6AD05C7C625", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "address": "127.10.0.5", "nickname": "relay2", "version": 4, "type": "error-stream", "master_key_ed25519": "Pymu1Z1eZRWgkE42xzDCYFLVKNtY743GKZzt6Im0OUw", "circ": ["8E687E91DCAB967F6E4EE8E46E66F6AD05C7C625", "C0606B414423F9A2BBA2679B440056E3B07FEC85"], "relay_recent_measurement_attempt_count": 1} +{"msg": "Could not connect to http://127.0.0.1:28888/sbws.bin over circ 24 [relay6 (C7C50946) -> exit2 (C0606B41)]: SOCKSHTTPConnectionPool(host='127.0.0.1', port=28888): Read timed out. (read timeout=10.0)", "relay_recent_priority_list_count": 1, "time": 1553519107.2006955, "relay_in_recent_consensus_count": 1, "fingerprint": "C7C5094677013F5BC124183C71A482D0156CDCFE", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "address": "127.10.0.9", "nickname": "relay6", "version": 4, "type": "error-stream", "master_key_ed25519": "gnREYdkUN0uYI3zF8oB+Wwm4MhM6ETPX1hEyt2m2a+Y", "circ": ["C7C5094677013F5BC124183C71A482D0156CDCFE", "C0606B414423F9A2BBA2679B440056E3B07FEC85"], "relay_recent_measurement_attempt_count": 1} +{"relay_recent_priority_list_count": 1, "consensus_bandwidth_is_unmeasured": false, "time": 1553519122.8653083, "relay_in_recent_consensus_count": 1, "fingerprint": "E894C65997F8EC96558B554176EEEA39C6A43EF6", "dest_url": "http://127.0.0.1:28888/sbws.bin", "scanner": "IDidntEditTheSBWSConfig", "consensus_bandwidth": 0, "relay_average_bandwidth": 1073741824, "rtts": [], "address": "127.10.0.10", "nickname": "relay7", "downloads": [{"amount": 35737381, "duration": 6.648009777069092}, {"amount": 35737381, "duration": 6.613178253173828}, {"amount": 35737381, "duration": 6.452952861785889}, {"amount": 35737381, "duration": 7.050682067871094}, {"amount": 35737381, "duration": 6.482200622558594}], "version": 4, "type": "success", "relay_observed_bandwidth": 0, "relay_burst_bandwidth": 1073741824, "master_key_ed25519": "cj7V+PYPJvSANsvBOjZSiCvXuGHXFrpSnPqC46I6DgU", "circ": ["E894C65997F8EC96558B554176EEEA39C6A43EF6", "FC264325EA99D597FF94DA88379DABB64304DD9D"], "relay_recent_measurement_attempt_count": 1} diff -Nru sbws-1.0.2/tests/data/.sbws/state.dat sbws-1.1.0/tests/data/.sbws/state.dat --- sbws-1.0.2/tests/data/.sbws/state.dat 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/data/.sbws/state.dat 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,9 @@ +{ + "uuid": "806218a0-3ce5-4778-b839-d6faf6798405", + "scanner_started": "2019-03-25T13:03:06", + "recent_consensus_count": 1, + "recent_priority_list_count": 1, + "recent_measurement_attempt_count": 15, + "min_perc_reached": null, + "recent_priority_relay_count": 15 +} \ No newline at end of file diff -Nru sbws-1.0.2/tests/data/.sbws/tor/cached-consensus sbws-1.1.0/tests/data/.sbws/tor/cached-consensus --- sbws-1.0.2/tests/data/.sbws/tor/cached-consensus 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/data/.sbws/tor/cached-consensus 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,144 @@ +network-status-version 3 +vote-status consensus +consensus-method 28 +valid-after 2019-03-25 13:08:50 +fresh-until 2019-03-25 13:09:00 +valid-until 2019-03-25 13:09:20 +voting-delay 2 2 +client-versions +server-versions +known-flags Authority Exit Fast Guard HSDir NoEdConsensus Running Stable V2Dir Valid +recommended-client-protocols Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 Link=4 Microdesc=1-2 Relay=2 +recommended-relay-protocols Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 Link=4 Microdesc=1-2 Relay=2 +required-client-protocols Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 Link=4 Microdesc=1-2 Relay=2 +required-relay-protocols Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 Link=3-4 Microdesc=1 Relay=1-2 +shared-rand-previous-value 0 zxJao+gBmFMSezvz/VXkEWEQJD5b/z+7AXNCGoLFVW0= +shared-rand-current-value 3 zOVHVZs8fQfIcs1HSjWa8nwr4kls3aj+LNhJcE4vUPw= +dir-source auth2 4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2 127.10.0.2 2003 2002 +contact pastly@torproject.org +vote-digest 9147EE32EA7B7FEE195EFFF8DD61BCBC044ACD1D +dir-source auth3 8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3 127.10.0.3 2003 2002 +contact pastly@torproject.org +vote-digest 21747518E052E648242156E67793D2B75A8462EF +dir-source auth1 D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1 127.10.0.1 2003 2002 +contact pastly@torproject.org +vote-digest 9735B1C401CEED00AA4FAF84348F0E2DB88002A5 +r relay1mbyteMAB EXpFbJERFAdr6051esSLFswMzF8 g041Vrpm/01ukZ5oAW+KAcudmTI 2019-03-25 13:02:42 127.10.0.14 2002 2003 +s Fast Guard HSDir Running Stable V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p reject 1-65535 +r exit1 JwqGGr7SLsK2JRmLzNeyudv/yTw t7/OM287ZDaeItUVISJ9PisVHi0 2019-03-25 13:02:42 127.10.0.11 2002 2003 +s Exit Fast Guard HSDir Running Stable V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p accept 1-65535 +r relay1 Kr+6zmEWehAZpWyzWy4zYrl9hXA baev0r8l0sbx5foNr6L7jMQBz7c 2019-03-25 13:02:42 127.10.0.4 2002 2003 +s Fast Guard HSDir Running Stable V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p reject 1-65535 +r relay5 MrcXj3IB92QRqZ01UvNA01l9Vik 17SIRm6SJrDZDwBnb8FPqzqXD5E 2019-03-25 13:02:42 127.10.0.8 2002 2003 +s Fast Guard HSDir Running Stable V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p reject 1-65535 +r auth3 NeO4u3HIE1VkmuxYYuy37X79vFw DWMUpmsayhRW35zrevN7h1AFd2U 2019-03-25 13:02:56 127.10.0.3 2002 2003 +s Authority Fast Running V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p reject 1-65535 +r relay4 TWZOJH5TDKXNUXa4wabavJUx8LA xVCoP9aGJrtbCj39wMNsTTzufek 2019-03-25 13:02:42 127.10.0.7 2002 2003 +s Fast Guard HSDir Running Stable V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p reject 1-65535 +r relay3 aT9zGHYkvnYKrSoSxe2J2x3gRPU PRQtdZg7vi0s5AuBP2hvVwa+Hg4 2019-03-25 13:02:42 127.10.0.6 2002 2003 +s Fast Guard HSDir Running Stable V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p reject 1-65535 +r relay2 jmh+kdyrln9uTujkbmb2rQXHxiU ZyXj9jg668CPFfNGzP3M6R7W5eA 2019-03-25 13:02:43 127.10.0.5 2002 2003 +s Fast Running V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p reject 1-65535 +r relay1mbyteRBR k04G84o5HLcd+D7N4F3/XN46xJ0 AvNHG0R8ux15D/4pfDRafr96avM 2019-03-25 13:02:42 127.10.0.15 2002 2003 +s Fast Guard HSDir Running Stable V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p reject 1-65535 +r auth1 qkXBMCXAN/BW5zQWmJGHjtCIAjE aB5bSTH8rgroGvljDElcFN6Eauo 2019-03-25 13:02:47 127.10.0.1 2002 2003 +s Authority Fast Guard HSDir Running Stable V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p reject 1-65535 +r exit2 wGBrQUQj+aK7omebRABW47B/7IU dI5io9KalNyLFOFjvEoUbWvDB2U 2019-03-25 13:02:43 127.10.0.12 2002 2003 +s Exit Fast Running V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p accept 1-65535 +r relay6 x8UJRncBP1vBJBg8caSC0BVs3P4 X8bEx5uGoC+0zI/P1k+H8md7e/g 2019-03-25 13:02:43 127.10.0.9 2002 2003 +s Fast Running V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p reject 1-65535 +r auth2 57PJoAQNYo2siLAlGuYzTSjo9TE KDl2076V5kyGxbuYGsROm2LZKAA 2019-03-25 13:02:56 127.10.0.2 2002 2003 +s Authority Fast Running V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p reject 1-65535 +r relay7 6JTGWZf47JZVi1VBdu7qOcakPvY f4N5R25SZPF8qjpCYOQluKK1hjw 2019-03-25 13:02:43 127.10.0.10 2002 2003 +s Fast Running V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p reject 1-65535 +r exit3 /CZDJeqZ1Zf/lNqIN52rtkME3Z0 BgiTPSUaddDaGl1hWS9Vw6/jJTg 2019-03-25 13:02:43 127.10.0.13 2002 2003 +s Exit Fast Running V2Dir Valid +v Tor 0.3.5.8 +pr Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +w Bandwidth=0 Unmeasured=1 +p accept 1-65535 +directory-footer +bandwidth-weights Wbd=3333 Wbe=0 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=3333 Wee=10000 Weg=3333 Wem=10000 Wgb=10000 Wgd=3333 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=3333 Wme=0 Wmg=0 Wmm=10000 +directory-signature 4EE103A081F400E6622F5461D51782B876BB5C24 9734950A2091BD2A6892A6AA0B53BEF3D85E2D2E +-----BEGIN SIGNATURE----- +VQZNfgZ81EmCqJUTTNf9JyphsOX4JaMtMSuHQx0Kr25jtznLZ3IGYssptgDWeD9F +mCt3xHKQd1mtF9cxPtRKMhAb6Tn9qL3vI55lzYinxJ2/RQ0aCpJyeMaeVhd0Rgq6 +zz/tV9iCLBltksk3jZy7tl/Yo7NbIOV97oQNSRXHRtYtRr37ADvW/hC8jegtDEfc +Nw3u/NMJnWTaO/2L4Mxh3/xBQP+Yhy0OQC6korX1HT4SnC9xVEl8xjV2/ShdObBx +m4FLHd4qqoCXLfVHEMcAb/x2BO/GWdrREiSKX0//UpnTLzOFdz3+Al2m8+qDzSrO +7D9qaS8IohSEoIvVPqa+CA== +-----END SIGNATURE----- +directory-signature 8B85069C7FC0593801E6491A34100264FCE28980 EA74AA3BF805F776E2BBC0C81BFDE45E74D3281E +-----BEGIN SIGNATURE----- +ZoSZOhNf6dqDe2OAIXTD8WV+BX7hHX3XfX9uCAC0LTwlr4bNRXHvV15I1NIK6x05 +Ptzz3Hgs9xaBqwa/VtcTyapxXy5uGNwAWCVbIrXL6LCbILRI5qh6vKy+CTXtY5Pt +lmitMjstIMXTQ79WmGiIQsPlWM4w5zeD+T6g5E0RYdKylY1U3wDBXBc4jokrCG64 +mcY4FYQgn1S6VrAJZLAvQ2H8l6P7QqSmVQk9T4EDkwNsbYsm1oIwkTVw0d+pEZLb +nZ/n525HDWDcWjeAhsicn9ptMMxBjpRqjxUnKZPPsQ95pWJ9a7T7/U+upuIuq7V/ +1cmRHWdBJwGHp0JISuzAJA== +-----END SIGNATURE----- +directory-signature D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 8E164C0799EA8FF145C8631FED8A4F512A3022CA +-----BEGIN SIGNATURE----- +Okb5iAC6le5ERMTOMxt8EqLNlLGJP7E5a/YdIJcjB6i33EXskIp5UQX69OQmEia8 +B6coFmof0UTJjR/KpacgG91+aE7mN6HNFogfWC8XIz+XFGlvgL14mQsvhv3aIXXx +NCpGh1FSn3G/M2MyPVSaJhtCWfGBXrIzk3twkPsztWl4jOnSG+Tjsvp2PMnZXoXH +gYhJjVDG625Y6oKbwVwRlCzidQrzFkX9CGrcp4o8UopOAm0V0W088LT8IcPLgvTH +E/zY+w75r4jQXbBFUlfy2EAZ1wbP7CSBW3DVA6LZdIcO7domb+5P3aeZLYtmGulO +4FLyzRArnIAdNEFKsE6pGg== +-----END SIGNATURE----- diff -Nru sbws-1.0.2/tests/data/.sbws/tor/cached-descriptors sbws-1.1.0/tests/data/.sbws/tor/cached-descriptors --- sbws-1.0.2/tests/data/.sbws/tor/cached-descriptors 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/data/.sbws/tor/cached-descriptors 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,801 @@ +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router relay1mbyteMAB 127.10.0.14 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+Aa/Y0Yi6yNl60hk0I5qHvifFw45lSzGfpgRREeh2DvkBAQAgBADZ9eIX +hP33eL69XBlkRDh9h5743w5OHMIZ5py+dVo1uvL3+Kdk/F2KvUFu1v3PvCM4L6/n +idxlpRjpBjYPJAp+O9gReRtgyuE5EvtC12kBhhJ0AfcHCoa0rb3qcDUj3wc= +-----END ED25519 CERT----- +master-key-ed25519 2fXiF4T993i+vVwZZEQ4fYee+N8OThzCGeacvnVaNbo +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:42 +fingerprint 117A 456C 9111 1407 6BEB 4E75 7AC4 8B16 CC0C CC5F +uptime 0 +bandwidth 1048576 1073741824 0 +extra-info-digest 3897F8831FC843D3B8E4E43E4C0024B3F666F85F bevCKiyzObGPKWTDY6m2Sr9YeDhczrQIkCa498wRw3E +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMGz6rMK5e4683h111qtOeHX40ikSl5JaqlKZTRXt790KAnTdNIZa5Rj +6sQU6fM6t+9jLSjDv55La8Ka8MuYtKnzOEnFrEY6K0afWJi1SCH1Usda7W9r91a9 +OqSZXc7GBKYp0Aa5YR9du4vnv+4ZHFY5DG0dxKTJJsUJNuoldjMTAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBALsJJ+zGdXFKBhOUv0i1C+MSfB6EZRW7zOvxNndPoNX5Ql50UI8taF+o +m+jei6vOyRes9hRZK3tTiT98LTXXf635Ad7iWZGW/F+owLRbEe5dtcjZ+AVflgHJ +gf0aYSdIO6qnGOmZhz8smnY1UcH/DwrBo6RgIl0UuMbM8oJyAuPxAgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +QjhUPn1WHyw96LbKUOsm+hEic1TAlVaLJUG4ndyB4ujgP09nGJTdEwfPh0IoF7ny +YEuda1H3Yw8wVgikG1HuuiaN8nxZ8lssmf0m3nUTpj0NLLuyAt2eBRzbgR4gSDCD +iqibDcOeqCcZowIZuGDWgUikwwRRs4qAbtHJi1dRScQ= +-----END CROSSCERT----- +ntor-onion-key-crosscert 1 +-----BEGIN ED25519 CERT----- +AQoABphOAdn14heE/fd4vr1cGWREOH2HnvjfDk4cwhnmnL51WjW6AEykFQ4Z4jQX +tt09tfuUha/Dzy1uIBXMktoxG2LSU5phbqtenL88MXnTtpY4rfUIKOeeMJmuRfDo +B7QgFEfFPgk= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key Hp/2KQ+kf94FGoGcUgDz6Q/Gco139fH85cZIdCANm1g= +reject *:* +tunnelled-dir-server +router-sig-ed25519 6so7MC5nK8zRyKkTz+jK7/rMpeFYqB1mqx5gTDzB/ZzOYgu16kmswQoh6cqauZKRHq3UC9AbxF2K5jzLOq1iDA +router-signature +-----BEGIN SIGNATURE----- +KP7kGUcKj2kw6ekJAvO1Jfu8wYMZ5QNvTE6i9zfHXzcoigN22jqtDUP3jVSNLEuK +Zwch0sJzqOo2TdnQCZQCltYPsuoSxIsIc4agxt4tgpaaU76my99pqt0TelDwN/NS +M3TdoNJ6B9lDFx2H3qWanza4Skf3tGl5exP1oeruqAo= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router exit1 127.10.0.11 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+ARpWo9ciBJUdH0NBQXH9JGEHonhYfVnaLeQTDn3tbiBTAQAgBAALnTpg +R1qwMNCqJ27gbZUswU7BqoxfbwFiS7GvX7Aj2iaMxh1+uUhHF2XkADgGJDU1UkaL +DSo/NkJcHfI0hHPO9qQjTX+KDjvtcOQtjV8oGqTrRtLUXIrF0tKX2uvo5QU= +-----END ED25519 CERT----- +master-key-ed25519 C506YEdasDDQqidu4G2VLMFOwaqMX28BYkuxr1+wI9o +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:42 +fingerprint 270A 861A BED2 2EC2 B625 198B CCD7 B2B9 DBFF C93C +uptime 0 +bandwidth 1073741824 1073741824 0 +extra-info-digest A5EF3135DA7348307A1FBCF271633BCA06AFB0DC abwjhxW5ju88ygG2gbLGkwFMjG/IIz6aUjQ5WwMXAjk +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMdgbZ7/Ndt7chWPH+QnlypWZzinXWi2clQx2YYDaE/46LtBsQnYQwc7 +2YsqxbJfjZ6Q8KNct5gXg0btAYSEpuz3LOd4ghLfu65wwaB/C/lAtfkoNYnm08s6 +1mYiObiQmyh2Pr44W/3lSbf5A1sKgFMwAvhR+FdLK9I/w1jTehsFAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBANSGu2AP7o3IGfskS8+xYc2MY0vN6gD9bBqHqP4EydZkPJTwsMTJ/+Oa ++ZlrTvgwc1dHdlJVuVOrcl5Znzsl1X7YD66LC1U+Lqo5AAAMS/EMGIJOfN6ZaoLC +5HUG514p2p2QCtkzDnfnbGjqbWndclBLJZ17aphEUERf8QQawQQZAgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +AUIaLmZKXcZjRuMLX5KJPEF4EG96xrsCQtULieoEdiLjkBNC4CnKGJnIlsaOERAd +jSoi/8nZjhRQYsp4Z/gMErCJ3WaGjjX5y5Z6E/cVnTVjPC4mRyMZQU/fcD8y00Q6 +eNbUPCa/tfRSouM/+jcgZZxgSZ9C2PevTge+QKAYTVU= +-----END CROSSCERT----- +ntor-onion-key-crosscert 0 +-----BEGIN ED25519 CERT----- +AQoABphOAQudOmBHWrAw0KonbuBtlSzBTsGqjF9vAWJLsa9fsCPaAONZ4YLoA8yJ +kzVqgAiUqniq6wUlxWBEtPJ+akORIzAfUCl7s4UdY5ECrvpoMgQt+BZ5wRQVmykT +toh9oCMo/wo= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key Igr87rZTZlJ/4Bh5BjMIDGGm6L9nUb4J/hEx/XXi6As= +accept *:* +ipv6-policy accept 1-65535 +tunnelled-dir-server +router-sig-ed25519 WKMIF5ydWjZZVuEAYBs5CyTah1233FawIY1KJ+w/RP8gbhajtDRTJpRilf/yrbj8/4gAo4ZmeZdyiyN9SP5MCA +router-signature +-----BEGIN SIGNATURE----- +KzcynGMe7yy9rOF09rR0t2aE6fojq7vZKZVMWA2v/Og21VJVf/rlsQhwt+9Cllrw ++Cre6YUzk2YS2NjvpxlYlUWGksc9ke/ea3HPNn2L4Pk7a2RQv7HMh0Rw805Q9sPS +yS4IjVEe1w56fyVvybAbg6zysIEgEkby6lpnMUFu8t4= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router relay1 127.10.0.4 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+AUdvarNdcPYBUrJ+XwmWCVdo8TfQkStNbaMNbAnKaifzAQAgBADM5lf7 +LIPsQ3Vb9jbCCO685ukU45Yr4tkhaUpbQA5wgS3lUlaulCBgUCsDRqh50SFGOXuO +UbGztWQoyh4iMGnk1BC2+Pktd05nJ1iIImJnZcXJdw7AdRy20Ss3r6KVbw8= +-----END ED25519 CERT----- +master-key-ed25519 zOZX+yyD7EN1W/Y2wgjuvObpFOOWK+LZIWlKW0AOcIE +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:42 +fingerprint 2ABF BACE 6116 7A10 19A5 6CB3 5B2E 3362 B97D 8570 +uptime 0 +bandwidth 1073741824 1073741824 0 +extra-info-digest 57B4758F9C604544B96A4CECEA68E5EBA7C87422 2FP0z0+tFKxfqY2S0kKsXfOYC4UPJWrbR26kKwqvWU8 +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAL6Xpwh8wFU8Csi+UxLBX/2LhlyyMJt6A7Ky9Dhv15d6fDCW4TTOCSqn +hHI+MBnBwYg/Mt25bPKAVlXByG6kX1jOaD7rsPJWzas0dtrv90AsLnb6n2uf5eL+ +B7WDENCvxuK5wT59xxF/BZfbidAYA5OqoXm26PT2iEf8oY1qO+8rAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBANLeAAwtzRB/YgKJTx7yNxk5GToSu/msrwnQemgbBl3sOw9i/o4zUbwx +W7AdDzCIXPae2Tqfn1i4fbrbF4xiAuavSgG+mnZWtKC9EeHJ5wZHY6D3TfXliU/b +GRv2QVVNtXrf3lpsiX4sY3wXe7t6B5C1tTAHlOdc31dyb6uTh7hXAgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +CupOCoyfUGO/AeJCHswUCZxoCSVF6N4/0C96hYGSqQ28QroVyOxQkJp+QZPF3elI +P8eCMrcAfVSUooJz5iEr0El6f/H5GuLdZAjlaO5rKhB3AjAnpPCQ/Yi/gwg1U7xQ +vBvxjNBk6QURDRzX2hsNLSePNKN6XjUFOti0iXA6e8Q= +-----END CROSSCERT----- +ntor-onion-key-crosscert 0 +-----BEGIN ED25519 CERT----- +AQoABphOAczmV/ssg+xDdVv2NsII7rzm6RTjlivi2SFpSltADnCBAFlMZ9QScnhP +dKPa1BvVay9sgW2/pOcjJkHnSoQk7hnIoOZuTdoAo8j1/ocgzRUWkaR7nLKgLMej +Q4ukfBrMiQo= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key nh2DK/VFifF3DvFuj3mJvLVse4WHeUuIiKV7/LV3Zxs= +reject *:* +tunnelled-dir-server +router-sig-ed25519 B+ltsC1tr7elhZUucfjq8OZN7YPmaXp2jbEzo7kUjJHlp9+DpBkj+MEFzgY5h7DuJu+Fq9rQZ84z3drqiVsoAw +router-signature +-----BEGIN SIGNATURE----- +W1jzQwy+kbrpRff/Momd5g395pRPRwNb/grI3tvZd65Uli4XuRxMBKlk2CGReD7E +iOxMKLTXzlLyGY0HIfH2dF+Pj+o5o5122A6sqJ8Ecaucd7jviWGonIDJk5X6MXGR +qnu1M166D9TPZptXcCn+lkIKC93tj7+8e2oHaBlo1gs= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router relay5 127.10.0.8 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+AZ+3pRqD8aheZv9V49udOeZEUnsz+ulAjdCeqqwmuorzAQAgBADk764K +kWgHOx7Lff5nOWDLLXz8xyZXWak8ln4OtkwpFzPoyrK91kk93vROLrGw2jdcp+29 +Q8YXzv0nlo82KUVeuGemrcbO5255DuTF7TlGRga6AD1bp2Ke8MZghhQeEgg= +-----END ED25519 CERT----- +master-key-ed25519 5O+uCpFoBzsey33+Zzlgyy18/McmV1mpPJZ+DrZMKRc +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:42 +fingerprint 32B7 178F 7201 F764 11A9 9D35 52F3 40D3 597D 5629 +uptime 0 +bandwidth 1073741824 1073741824 0 +extra-info-digest F5666EB8059B058875106492FB91F0C01C8508A4 v8AtF/T7gBCE2Vf/5Ajxmvj+J1QbZhr722X9JleKYCA +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAPFjYNupsxb/YjwZE7hT0XO38U7hehNpXvhnIN+kj9UAmuCXiWuHaUOW +32gz8xH1anfG2x3H/04DSXB87uq6A1MH3WNSQ2ag6E3uw0IzUf+KyTD6RrQocj7A +tIUnB5z0YiKjHwryw4vPCWulgfGu5dHrnnVtrJVbw6w3Ao6D2AX9AgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMaxtjKfv720Orx01vvtsfOs1kiVXJv3vfWoOE+DVTLM05dVDiR93vfV +MBYTFeGNglrGDYbMIho6vljrSliK4eIFzlvwIXHynjqWVWoxJWQt1FUt4PBdwR5j +sYBSgVmoMI+Ub/LwsjO6vp9YdKvCCkF3eyV/wXlzCYLYe2TSex23AgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +NGloVnbLb6OjFEaFHrkluTn9VyCyMBQ9fWWACFXpmm+oKPP07tpGNiKkQEvRmx93 +oqtAVUaaoaYkbsvRUC9LKFd5gJBUun6G8pqw4v5xo+wWfvvtmMOftRIOE8vVvo9X +I1nIRW6q7ggvBJN6cBHGc4PP4M9BUtxSEVdZ7jx50bM= +-----END CROSSCERT----- +ntor-onion-key-crosscert 0 +-----BEGIN ED25519 CERT----- +AQoABphOAeTvrgqRaAc7Hst9/mc5YMstfPzHJldZqTyWfg62TCkXAFDrGpftsYWY +t+eB5kXOo7TyzEjJvsRIMApdxsj+nwzsw6aX2CzTa5IjPuaVP1zU+7vIqhkQv2IR +DEXbdTxzzQc= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key hU3fUFYXJMCSmKBbTiI7BCPGqueAeHunamShaC11yDM= +reject *:* +tunnelled-dir-server +router-sig-ed25519 D29QAzMvpnfcSkMA72F4bYUhxxvnQMRshqKcKu3wV1jTUaiaeQ39hzeLba+helKd5TDn4IDbJPqwqH45t/Y8Bw +router-signature +-----BEGIN SIGNATURE----- +Xhcuib9Bo9Sp6X550TPD7kEfjKAlcNWRigI3qdEZFWsjerHfswyab5QT0XjsAEvR +Rx1Re7zLd8M5k6wdLFkG2COzHCffKYA76YvTfSb0vGgz2CslZISk2qzaV7i7TyuU +8BS3QM9N+sVFc0A3C4OHRp6jGgd/USq4/teSP51ZEqU= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router relay4 127.10.0.7 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+AQbp6+KbYm5Nd0p/P+gqlVlM//nq2t0MRHT2Zgigwm4wAQAgBAA1Cyl8 +Ar/GTLAUdXkuxDmUuLYGZMrHYia7iNpwnR32i1emTuV6fxWk0os6Iip50xuFrfIw +vChQnBLH6RD16Y9ZpFwT4V4qS+zFXEiZYipyG5+tjxPmguJDmLylD+w/QAY= +-----END ED25519 CERT----- +master-key-ed25519 NQspfAK/xkywFHV5LsQ5lLi2BmTKx2Imu4jacJ0d9os +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:42 +fingerprint 4D66 4E24 7E53 0CA5 CD51 76B8 C1A6 DABC 9531 F0B0 +uptime 0 +bandwidth 1073741824 1073741824 0 +extra-info-digest FC704D38B23093307F4C1686BDA90BE15A37C1DE ZimUgBYAYoy/3JKn4Kb4gKJCg4SE2DS19DI2Sop8WFw +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBANz9jsTlSwTP63thEv2vY5ZW5O9mBzfeZ8tsqwZGj2ipsEPZdlvV/Acw +jpxNEPOVSJCOkC922JkS8haQrbujRTa2PEzIuAHnNBKCBN6gr8DnTTfGbBKPGmBH +mTTEBV/EOS48XvgNpN0rhW4df6lDDGgs+M2YZFm72Axft2lgkZoJAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMQBopTlw49Rl/RWTddTFB/slDb27ojRTj+kc/awpOXmrYGTHaikF0Rn +QcDpXjehkQALqCzAL5wCSrdhhSRSW3gSzXJaxnNgQJg+xdssQk4vipHAoLQhGOmc +9KFr4r1VfcNn2b5Xpfl2U0D67WT2SwEVnMUR8OGoZdytb9Vh11MTAgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +UKyXSdk5Qs2l/MFlp18Xqfj02wGpNKUh5It+468y/j9um4d5RBeg2OAFG3WpI2AM +Tm9x5lPKNFpyEUAb4lOIT/4+C1Kmz1RttL0vm7H3MoWMczL/nJ9MLNS+Jv/vf5X8 +bW2E+iEApYRy7bzNARhA8ZHgSSwGcpZT4XFQzCtjjWo= +-----END CROSSCERT----- +ntor-onion-key-crosscert 1 +-----BEGIN ED25519 CERT----- +AQoABphOATULKXwCv8ZMsBR1eS7EOZS4tgZkysdiJruI2nCdHfaLAHgjQYmrwhh9 +MPGp2PIhbWta7vnTX+O1lSBSlwgCXJZL43AWaIMmM6Qm0MipP1N+avOwouQxwzz5 +yp287gZ5tQM= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key /6rXtx9cROFk4upSD80aHux8k+RyfSNZPWDFjL3b/yM= +reject *:* +tunnelled-dir-server +router-sig-ed25519 hIZ8kTxQ360lGMEe88TExMIOOJ0kR8IkYbo/Xg9IMfMcTZr4xzccc7+ZBSS4gC61SlfbK3VXeMVH1bnwrNhhBg +router-signature +-----BEGIN SIGNATURE----- +rv6R0N54uDuoWItGSP1v6r1SwqPJlKVMZncWZzkrAJQVm9E3wsekwiRPZsG4H0R8 +9u3vKYwHwagbM9JoXpqTzswt04YoUJ7H2Yo6WpCivjOvDtzJNAuNPMMj+5VkImQZ +LGOtwgqkMMZ7EnKxLS6CG8d38DCMF3wvpPpb5BkfJXA= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router relay3 127.10.0.6 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+ASMK9D+D72vsrNpuJTpb2797Y6aNNvCQuarCSq/vXZZLAQAgBAD8+tNu +l6fcGso2CI1GEBr3clC2etPcslfcprX6O9taPgghYJ2uwfYh4egjnIh9ugCB09H4 +rPsHJP5JUeFS+HQYV8OIL7Wud7TcfiLB3C883h+guSl9srRkF0qSMVwCCwY= +-----END ED25519 CERT----- +master-key-ed25519 /PrTbpen3BrKNgiNRhAa93JQtnrT3LJX3Ka1+jvbWj4 +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:42 +fingerprint 693F 7318 7624 BE76 0AAD 2A12 C5ED 89DB 1DE0 44F5 +uptime 0 +bandwidth 1073741824 1073741824 0 +extra-info-digest ACD8BFDD1A40EEFA60DC94B58829DDA3553523B9 bKkTYFrrxZKAglDuRAnexLMXnsoVUzhv2SvjeM8jNVY +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMOehJbGzrHB+Mq81PbDkI61eoj63pkTmztbloj8zh0nxyNaPaXdqMLS +BdPk4J6ygdezuB2xG0dP05pjN/7w0M0rugelc4kKfeak6WHSKKEIUgLC41f8sGvz +0eN7Q2iAwgmNvv7nYVDMxayT8+kqJ1JM0tMDJUQbq7SvUACInPu3AgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBANobRE2EiEqD96o6u9C9hulIZMuQQYIn3jiy724UDoOn572Cf6UpoyYH +QAQmDit5MbGiATY63SHwq/fdH3JkHgnjhOjKSmD8OPOJKXQXAjjt9POjlpQKTCxI +wf6qT6EaxtBpjKYSal53iMfA1rWIt2E+W2enamoNwOqiVBsRDF5xAgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +Qhs/BTOtTZdUhexY6azcniVxL/Fapzu4pXvgJAmSP2jfWMtgI0BRdpgSmjgjmzi/ +F5Wf5kPX4j4iY48ysvDfbJTGeXpOw9jgLFIRE4IkyDgRxreozDqVNlt1p8ur6Mpz +43xs7lTex7twdO4aCSR508wvG7NQ65VNGNdboEw9YHQ= +-----END CROSSCERT----- +ntor-onion-key-crosscert 0 +-----BEGIN ED25519 CERT----- +AQoABphOAfz6026Xp9wayjYIjUYQGvdyULZ609yyV9ymtfo721o+AJYvtRtZhndO +wrksjFTzLxh6U6fGutIl2yPwDTnAUZBKXN/5q8EhZx2yD33OJKcwGrMNyUKpS0+Q +0qpila2p2AM= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key vF34QJkBD8So4nk8g0I8E8J5QSexrIHcIKLmKIntV0E= +reject *:* +tunnelled-dir-server +router-sig-ed25519 UeEUD6dEvpu8uR8tTzyY8I8DvbVOtYsT2M3cdLxPK6Vy2AmvftYZm//SMDtOEG1UOJR7bEydzz+wwFHCbNb6CQ +router-signature +-----BEGIN SIGNATURE----- +yAglv8mngM/rd9/iTglvEcA2G4pn9IbtvW3DrUVCuNyUAkCZbArlfJ01gctgytQD +6zhOHjBkT3xmqxWiqJEOV7wMUXELDLeLSXrdVRP1pAriWQLdHBq0mggLtO1AIzkr +H9pGJ1yw56ivBN/WMCT+goQl/HkSqqt/ygr8mvQbMBA= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router relay1mbyteRBR 127.10.0.15 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+AXbVIoFf+9SrkWrHFdCukwtqfAqM8enm1zKWIQ4HwshgAQAgBAB5HQed +iXOk4QbDG4WNmQYlnqm7Z5XWLp932WEXhQ4yeqx+ayKTyDvsUc8rOO+iNw1jAmeS +jZGFSrtG85MtUrno4WctkerE1CmddAor/k6agL1s1QfSCUHhFzBVbp7xsAU= +-----END ED25519 CERT----- +master-key-ed25519 eR0HnYlzpOEGwxuFjZkGJZ6pu2eV1i6fd9lhF4UOMno +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:42 +fingerprint 934E 06F3 8A39 1CB7 1DF8 3ECD E05D FF5C DE3A C49D +uptime 0 +bandwidth 1048576 1048576 0 +extra-info-digest B787F30DBD0E2635D768FE7AC1D4757CDBE229A7 w1f/fSCVwsuHXqvy5rSAUa6wrFK43pa4UYq0C0E/Ucs +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAKZP4AIDlANE+rL/U7+H90QNtA8atk7s0AODro2/dKhCD18lwz9ZN2oK +BkQquin9glLaq/7cNMxxrtx4DIANlSfIeHobisFIh4eOmf/c5a+s1i5yHtTBab5k +/YQf3jrm4QEwhTnEUptXcNPBdhv5yQXtpin+RHthSc4V24Taz3k/AgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAKTcXIi/sknfciXc8macS7WHmT7sucXv0HDWKyMmWfXNG0owMW3T03MC +IzSAZozA6GoiCL38yVJQUgR9aL/pIV+J1sO1ZvmW0RhllAnnkVMSUi0s/I/PsMml +TpNMYH+Nt4a3ecxBzEBRYmQF8DfU39rOLf0P7a18pp6IhbFz0hR7AgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +F296Dg3YlyAjT2sN8U5T/252PCoQ2MyExrEN7ykXd8YMtq3rgRT7H82CCWP/jKER +LniC1EClldKcLyAy9aXvEbmLuZKjcvesmrs5HuHfwrrTzaMa24dcQEO6/bNen6S/ +CdrL9YwymyQBgVkeNGd2SzSIJJW9busL3XTBv9x/OF8= +-----END CROSSCERT----- +ntor-onion-key-crosscert 1 +-----BEGIN ED25519 CERT----- +AQoABphOAXkdB52Jc6ThBsMbhY2ZBiWeqbtnldYun3fZYReFDjJ6AAnISzCIv2D5 +F4tRzcS5imY50grXbTi6DklFu2eF2ebXRsWnz7GavTqYAJ8kEenYVTjJ3fCARTyg +fGrb5VGmSAg= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key Lo1mKXhJlVeVSLjTVBTIHsptAXgoDyD2Sp1bTQWe5QY= +reject *:* +tunnelled-dir-server +router-sig-ed25519 89utupSelbpTVdRjzVm8DhzSCPIA1wDsIwN5BxChIDNYa9Yvs4B01eJfaajG3RXwy+I9vgv31c0O6PQllf/oAg +router-signature +-----BEGIN SIGNATURE----- +G0bwgmcInwG16VrWATXocO5u2fXzZvOz/nZ1p6nCRAFdJAiDFxck61YknapmozeA +6vKBgSuSun2pvq8Cr/gssLEb/NY7SD+VExvGiQfO/m582Ry/int9yuJplQ3mDriY +LdAI7N2qRWirnFg52m0qVhhe0+IJmoDiS4OnT5FKC4c= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router auth3 127.10.0.3 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+AdeKc4xq8+u8Ztz0hORT8U5t1GpKHjtzYs6sFzcPcL7HAQAgBACvb1Fa +UUwh1TkZS8qxYZ2FAgqMilTOqDDTI3H/sik8U2fr/oCLUctr3rH2hR5ztHZpG/Dm +GMC/lMLp4uz0n3I3wGtEkrOFAT+TecOD96GvJmQhllUzAymEDbjRmq/byAY= +-----END ED25519 CERT----- +master-key-ed25519 r29RWlFMIdU5GUvKsWGdhQIKjIpUzqgw0yNx/7IpPFM +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:43 +fingerprint 35E3 B8BB 71C8 1355 649A EC58 62EC B7ED 7EFD BC5C +uptime 0 +bandwidth 1073741824 1073741824 0 +extra-info-digest 5D024D072C8D3DBF305742ACCE4BF859E736CA6F DxHNlVn+EK17ECpg+hEEFhqoPFEzaMSotw2DaalT+Jc +caches-extra-info +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBALcC2Pd8WPW7ChRrwu2bFItceyhO449nnP8nG2TVcyi29jogkcbE9HFQ +850W96EGTr4yG+E9FmB6U3xGZR0TnQd/776jztHBKjQOSN5ACfXTLGH2c7gy9jKK +MOyx5BYNLsAZrGKtY5qwynI1i7cvGy2+i1IqfOepacjiNgTgkfrvAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAJ3y+s5tmFd+bDUZtKF3GLY4KRgf80L0JqkjWs2NXOtWSoToQL0c2xCZ +2BEPs0WV4W6Qmh/jGclybfcPVO48NXUIgcqcaG0hUjG/5SU9DRpWIbCoA0rR2oq9 +ZXZU6teEozTDVKsSqQM6uavN4cL1FCN4H9wH6vDwD1GB4WQuM5m/AgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +M8T3n0Pl9v4I8hYuJXkBryvl0sn9T+2qrBGAiAREsJ2kqJIuDU0RzZC58nRtUKcp +dktpPRmJ2Ua6IVSgcRJ28zp06fHhVfIIGACySfG9nFy79ABrcuZpsvRTncFzxdfU +e0d7G5IdoV6VZddPKcQ766L8NTC5Xg6kLy6oBjc0Vec= +-----END CROSSCERT----- +ntor-onion-key-crosscert 0 +-----BEGIN ED25519 CERT----- +AQoABphOAa9vUVpRTCHVORlLyrFhnYUCCoyKVM6oMNMjcf+yKTxTAGnSSqJ2FQkH +WfaMp+jIfk2XQVqURWvbhg/M+RRBMGqlH1RZZ6MYM7z6yl29NhlTQ4h0jFQ/BTP7 +Q91AMglu7Ag= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key ovVOuUzu7wTFq9kFWe/z8l/yzF+xseCHSU+rmUCuERs= +reject *:* +tunnelled-dir-server +router-sig-ed25519 fY2oZ8T/OBHINeby7LURqa1yGGedK8+mucRKeQTWGdCtpM3k7dz9iYAL6l2ng6J6bAkhFp4Efl4aPLRotNFZAA +router-signature +-----BEGIN SIGNATURE----- +ERERsQ0S2ii7E04SoIWx8PuPBq5Q6/nZwUX54Aor0up0p6V9zhgWj/IoY8r3eCcX +sO+U8TpJSI3bjhO+Ggi75Tm5udULIwMgR7HBTzImZflMmBuQy1rETZbFFFV9J1J3 +RAB/Rmh+QjnEOITKRiq8RwW+X0m3SMsKP6A4rqWx9nU= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router relay2 127.10.0.5 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+AcEGD5KhGrUHV9K/8pw8altHi1QWEHwDaVVg67odDegNAQAgBAA/Ka7V +nV5lFaCQTjbHMMJgUtUo21jvjcYpnO3oibQ5TMPtPCjZjP6WoujGtH/zqv/DwIsw +VPP/PzdAUYUTkH71islhURRiO8l2FCeiIMM1/gjv7mhPAftydv3+3QcJDwQ= +-----END ED25519 CERT----- +master-key-ed25519 Pymu1Z1eZRWgkE42xzDCYFLVKNtY743GKZzt6Im0OUw +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:43 +fingerprint 8E68 7E91 DCAB 967F 6E4E E8E4 6E66 F6AD 05C7 C625 +uptime 0 +bandwidth 1073741824 1073741824 0 +extra-info-digest 410D967EF4F55C1B381BC899C5C31D4819C96060 +bUjSUbJ7OYipMc/gv4VoYrLdZSvLX0IdjKxAgT8/rc +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAPqBPW2d3hJ6L4M29OUbL6gf0JAycoI29ObZ5s+M3cWA61rxtMOe/D44 +8N/LNltPLHfT19FpuDUaCu1I2rvjywHYmi01FernJmkBKsSr3SBrIa0+Kdi6fLKl +vsvNR6dcfvm2nKlt1OsmLj6+S+fyw/Vtswb+UQ+UQ4T9SNEVZc2xAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAPFSB2vre2FfMsMDJwu9TgvLgIA4f2vXcOVNpYfeCAcUaQEPzkqQViWF +ae6rb1qrpT5iTgDmbD0qZbR6QTakQAJSy9wddNLkQOoSQNdzWXpBfSXr1Exh+R9X +ArLpYvGvtjRE82pySnA/x76DaaCxiz5tiZSOy9whWxX0CMwI1T1xAgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +iejOHEfqMq9/kRmWh19omSRGiIfedHl9PGFSfhHbT+UVBgZYOGjllfgFLCHucYgi +F8fKSiMKEmXvnCekUFtU8MKQfKCzKp40s0XLzWHQjzXbKo4GkKqt9kkVRf1dN+CD +SlZyDpMOVwt97XhB6AwToXFMZjCpIuqWGY7DI3S/k38= +-----END CROSSCERT----- +ntor-onion-key-crosscert 0 +-----BEGIN ED25519 CERT----- +AQoABphOAT8prtWdXmUVoJBONscwwmBS1SjbWO+Nximc7eiJtDlMANyhlZJYzq0N +hEEppB5MwzmQeA0yZieql4XPfJLXnBXoeBIzZ0VX3NsPlWsYJr7i8pQiF06hk8h+ +fLvtq/+/tws= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key 4TDufNyl8LWyZjQpMRN6ChtjnXN0s09I6LmawoauEGo= +reject *:* +tunnelled-dir-server +router-sig-ed25519 GymcEBnuPpcBg3XoQ5ReZlXbFEld1Vp3s0ddS3BYJB8znhyC+BTg6OetikFCpQ1PGsPYmKQ8HgvU8MoHPTPpBQ +router-signature +-----BEGIN SIGNATURE----- +0roFdqEN6C8pFbY5AbgXOnpC0ggaAlA+F48Xr0Abxk7/fGb/+GLmh+kKmwy3LnRX +cRrW7IpR9FksObAj11I6IPhy7Chk1wE5w5HKBB5ZkQz0KS/2QMJX4hmiTmXu+1Hu +QfUUxv/yWo+PVYM5De7KLCcCnD1O/WKSauc2tSi1GmM= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router exit2 127.10.0.12 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+AW9hylFVCrg6uWnIM0X/thbHuetg7bcFIexCSiHgyMnRAQAgBACKuIPy +wgpLblYZzqYt6xDaiRPVNyfzUC6qcRsi0l669ldsSvNNiPzYD+OjNlr/pVd5wwJH +R9geai2pX3cEgbXYoxPJCw0rPFFbSq6wpnZ3SHFZjlemHOy2/bFF1WzJIAc= +-----END ED25519 CERT----- +master-key-ed25519 iriD8sIKS25WGc6mLesQ2okT1Tcn81AuqnEbItJeuvY +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:43 +fingerprint C060 6B41 4423 F9A2 BBA2 679B 4400 56E3 B07F EC85 +uptime 0 +bandwidth 1073741824 1073741824 0 +extra-info-digest AAB87CAAE4DFACCF6E4DECE556B3BF9B581F48FA lxPAa0nClDkbrZR6Z7xZZBGMYvuVlFIGZ7ylvczfJVA +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBANEZuLyNdoR1cAqBLreTHuqU8mlbUZUvYcdUn/EgGuwM8n5JMpUTfWKy +TjxyD8WFxb3WbIlVr64+DkLJGcDa6kTPmQJ/xbso/KJ9Xt9pV6UFg7+sqDN1SpdG +NwwLWwXdGk0uBEVsH0kQygsQaoX5PUlUfK+wdpeEIaSiy9G2b1rNAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAM9JJH74Bdw30YrwnrmJpCY88QlWnz3m2wYPacL0gLbJ7kzBEGtbryCu +XsmrSUraEE0/j1X4mkaf3/O+i7Fn8ELSJc885if1R83zRrQEiVvovmH0Ln5hInXw +nixOTg2X+PYKbk1gMe3rthDTZoFa1ruETaGwviiTK0Km7hvxaNEjAgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +dVNrF8tEf4KOKRyG45NI3/bfXN3CKxKzI6TuykMDqcFUMQ5NKVuH/tX1zlZg0tT4 +nqb4hSvzOB5L1KSphqaYPDn3g23UZA9Zy1yHXsdCCKHyQpfV4qLgUCTGi8Kj8GNd +Vrrx+U6i0kRz5EF4IFWEwQqGeH1XFpajrb6Vq2Q7sNc= +-----END CROSSCERT----- +ntor-onion-key-crosscert 0 +-----BEGIN ED25519 CERT----- +AQoABphOAYq4g/LCCktuVhnOpi3rENqJE9U3J/NQLqpxGyLSXrr2AJX9Bitugr9l +OlRCkAfAWNnVR3leruTXqri14FZ7e4prcBKO6DsmvjI5G9X7bsQX2eHfdaC0GaH7 +rt67qvW8qAs= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key 5V8GcgFQLqkuZ9OHSeTPSuV+/rnQRH78QJa/pczgmj4= +accept *:* +ipv6-policy accept 1-65535 +tunnelled-dir-server +router-sig-ed25519 OJbfPsESbossJIuL1+x0HktEVwicxDSi0Bp3oWrRbj7QigpvYSBjkWExVSg+Lv3UxHE4WcSJ/yWFQbLBmOInBw +router-signature +-----BEGIN SIGNATURE----- +f5s8DmIi6Qk48ZVm49HGh/ceSlzYOCy3dWjUqOFCJ3W81zLlscdozXJRseRlLHzu +HsgB3OSPFoAEI0cHKdBd7vEMFkJAc2OqL36p03oK3la3vfDJc1VlUwMX4VqEToBD +ZTPC01OMBrbTAqyYd4v6w5SRCY0XUS1hkBFTP8IdbDU= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router relay6 127.10.0.9 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+Ac0trpnQdBwDqtM//X1f9IadJQ/qXHi+kcdE0+KYqClUAQAgBACCdERh +2RQ3S5gjfMXygH5bCbgyEzoRM9fWETK3abZr5uPVroZvXDuIGSEgc9Ecx8rDALPt +YE2h7AdthCV0LJjVB3OWVZm2vi8/5wKU+s48pAFEOGl3UVBvzYcl5LJ6qAg= +-----END ED25519 CERT----- +master-key-ed25519 gnREYdkUN0uYI3zF8oB+Wwm4MhM6ETPX1hEyt2m2a+Y +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:43 +fingerprint C7C5 0946 7701 3F5B C124 183C 71A4 82D0 156C DCFE +uptime 0 +bandwidth 1073741824 1073741824 0 +extra-info-digest 8F934E45BE27725AE81FA5EB4C0FA13BC4DC65ED T/kgrjXUbGa7ggVgc55NNGOJr18SIa+OiaGu5eLTOao +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBANYuda9CTBRG2fcsGAvrc48LZlDiROkyq1HFweDHPfusU+bB5RdpWKmS +3a/ZgMqVstg7aBoRh7F6C1rehsM7mDz7DoNNnYPNlJw/KHgtsDc7jxVLfVP8w38J +hhC0Gkgm8pm5XrVMll/8RvfgqQiy+eDI9a+8iWP8v7ZLm95OHjnJAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMzhYKspx3To9ffg7o1JwUChSG8vDfq8TiS8M9xuNoJS2Dh/9gy7E2pF +iJHIXoKucrxBDT4EgtJjd++V5rKr6gwg46nT/qJdk3YMm7JRPnN/BGBrF+dEQCef +hveU5XlE4T054CPlBKV/vzoPPqvVtKvlqcM7WWanvOWslpFf+6j1AgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +GFDVgbL2NE2mYuMMudwLK6F2ag59XonrN2XYPoIY6WSkfd5FaySPx9EUqSYkZToD +zzpBpyaiVDJtHbuOiy0yXJcMpBNbzLAieVqz6P5liJw7J1fQk5g3dUYvkV3peCtC +Efu3kk3Ggq60zmgc52KVjZzfzuCw1gYEyQBau9FHIvs= +-----END CROSSCERT----- +ntor-onion-key-crosscert 1 +-----BEGIN ED25519 CERT----- +AQoABphOAYJ0RGHZFDdLmCN8xfKAflsJuDITOhEz19YRMrdptmvmAAFG1hSEBkjx +cXNXjuoTKzArYbZ+NfT9fnRukH5K2D48jRCJLdttvUAsH5yI4iAMT+48gA9jO8Hw ++Mj6TyV3bQg= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key F7zm1LuRkkxsSyEk/8XPHJQPzqutX3Mx8KqbcOIgqQg= +reject *:* +tunnelled-dir-server +router-sig-ed25519 uo6+Xl9m7qPNST/dDj+JQwISGo5v3BY1MveJ24SLrYpDrkJpNziIgH2pwYLwSild3a6dUi5M5wNA2q69DNY/DQ +router-signature +-----BEGIN SIGNATURE----- +r3AzYLlVX5FU22BMJeER8hg8h7JTkLdDOE3iyz6RuhqQoo7fJjd4tY9bBVVtZRg3 +4qHU1F8xnepdxa/rwbCtf6fS1DMddVX4h6+GBhJRfMWi4z6CfgM0LmSmJj/Ecg5x +ehuXeyw82lRB4SzRnWi7Kty+A3edLLuSJqCblYSJzkk= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router auth2 127.10.0.2 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+AXC+MrRl/H84Oi1ehh8oZLIoil2MFWfK1WZl/jhHFlTWAQAgBAC4/Pxl +k2bYZytrsFq4k/k8f7u4A1GtpikKPs0W2nUp00ggHkWwP49QuWpTcRkNcF/oxxWD +NQjooXTK1/kPSX8STuS0v650Re/vqbktE43LagJrfrgdWlzZAkRJsuhWrgU= +-----END ED25519 CERT----- +master-key-ed25519 uPz8ZZNm2Gcra7BauJP5PH+7uANRraYpCj7NFtp1KdM +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:43 +fingerprint E7B3 C9A0 040D 628D AC88 B025 1AE6 334D 28E8 F531 +uptime 0 +bandwidth 1073741824 1073741824 0 +extra-info-digest 0225E581F430F793E888435C4A2D8ED110702C0D mEo7kO0Au3OC6BtODL5yoQC+g3FUNHuOFAUlB/sfXp0 +caches-extra-info +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAL8QRs6ivxV/dQAIlymYN2+tKKX+GixsPXFFDSVFuq4KG448hPoh9irf +/qudysiq9AYXyc4whhkrrVZJP3tW+Cc9g8hXDCVxRFr9I7v48cU2F3iIOXfmWpO9 +CbXO69foc43AUkmrE+JBSw8ebYrRpBwwqUCiPMPakVkO7j7z4NO/AgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMWmFjVZOuY1EaBGMYc/gDANRhD7FjzlKwopwLuQnCyCJWmk8mpJL0UJ +LZgk0xyz+7XgiE0ohG0/xUsj+u6uT2HZCdttvivgVJ/QMGYNbxCHNqD5RS5pXb0R +YH9Ojok7hzGbqHOnPP4GXcAmxk34u7OCaTpl8DLt0DaOu8CaXyzVAgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +FeOqXPRqQO4MqMyx+oUfzT2qniBpjZJU7Akja36sUNLXDULaDJBJJCka6FaiBnF8 +6Z0p/H62LqTQjYc18ljrNpamOpDqgnbYxLBVpp9pMjsZDReFcgOYkRheUGOF97DR ++HipH/9qbXWNJ4r+Ik5FsYIv4FiZpBzAomIwjHrsxYc= +-----END CROSSCERT----- +ntor-onion-key-crosscert 1 +-----BEGIN ED25519 CERT----- +AQoABphOAbj8/GWTZthnK2uwWriT+Tx/u7gDUa2mKQo+zRbadSnTAH9eekLCPGN4 +Uu6ur1jxUWktzls69geX4pi5eD89NDN+w7nq2X++PISYPvq1ga8OhP/+RHv3ayGt +El2zEwIRpwA= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key UDhxVljTycByYSSw2T7eWRImIMuA0to9UVVS6tbxgVk= +reject *:* +tunnelled-dir-server +router-sig-ed25519 v9loEs8EQQoPt/Z24dZNhElHr295OwToLr9QafOeF4VilYWu3BoFDEjKZb8c2czzksap4Cg654I63tAMP2eABg +router-signature +-----BEGIN SIGNATURE----- +SoH/ynyOtly+rBImK/FMvNz5mQxcr1supDl42CGE5KalzNm6/MVH1ugv2MyMQGNi +iuvQjj5VC66YXyV1NMGxnUhG8pfmy0edcbXJhbUJf+oUlnamo60mMjgjG1E72gd7 +hjskluXyOMXH/MPjEWhZcL0D1BZUyn1spd3mKInS7po= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router relay7 127.10.0.10 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+Af2e2I1NEwFCS8g7XY9yRL4rm0WLQOo5eg539lVGU0fHAQAgBAByPtX4 +9g8m9IA2y8E6NlKIK9e4YdcWulKc+oLjojoOBea6wnAermK+oGlkoYfMKMaoyQQQ +J1fNJURDuNkFL5oUEKbXkYUvRzXq929cE6ssMeIlRUHTvqaNLiipU9tdCAk= +-----END ED25519 CERT----- +master-key-ed25519 cj7V+PYPJvSANsvBOjZSiCvXuGHXFrpSnPqC46I6DgU +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:43 +fingerprint E894 C659 97F8 EC96 558B 5541 76EE EA39 C6A4 3EF6 +uptime 0 +bandwidth 1073741824 1073741824 0 +extra-info-digest A08B4A5D125CA8728F387290E268DF80E806E5C0 gYdEf22bQ3qaSfFqTURkW1j4a6oHp+FwAKPlY5fSgiU +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAN63UnkXXtULS72ZXlfzGRN1B5N3KRD+/dnG36BxA6AX0YXCClLsdSen +oFIYSNlnO321hlHSK3Za907iDEtctZvaOqjrMrRFf1ukf1QXoCbAd9DPwIEkVG5b +DoCjbu5lallmbH/TvkWoBe0RuhfCmABIkVbjcdVCAe6CAcYVHWDFAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBALzD6GgnLtjktO0gpd63iTuIUsAeLH2H/RT5CtQnGktYAnaq4Rtqe0r/ +vIW779dRm0rvRtKmoJY7O9W+NWPNoIrSGYBueTwa3wKKfA4n4pDVbq2rYPyP0dR/ +pQdG3qWqBO3doD0s5mTwAU2KGltXL2kjlXo9fR9HWmFJVcesuxeXAgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +dCDzUKjcFw3CeqsQeI2RCTgYFwHSviMwHoWGrgt8X9Bh6KwRUFYyEwBCXAh4zwQY +yi03xAZIw2dqGdasjGje7Y47vRzthLEpn/6q1j8mViqb2ZVCkDj98+4S/VlVgExP +TfnrEqkyMYLki3Ftsx7ABJaUsFm32w1ehvFSll7rYzg= +-----END CROSSCERT----- +ntor-onion-key-crosscert 1 +-----BEGIN ED25519 CERT----- +AQoABphOAXI+1fj2Dyb0gDbLwTo2Uogr17hh1xa6Upz6guOiOg4FAK0rb4grCOPL +2vi79NNnCX/2Ghy8b4a7zb3JOThGwHgzkipNBy6OF0RH+8/91DTX8w0XH8c2me4G +smdIGrR/CQA= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key SrHWVe4cmcXboSRu2Fmt+d60nFKlVvgGUIjWG0SmwEM= +reject *:* +tunnelled-dir-server +router-sig-ed25519 XmUZL4s09pbx8cbyfthK7OEPHMR6lHDveC7JyfZteqWnVkKudgwgqd4Tb2GgqwqQPMacqUiD0XAGZn5zxZ9nDA +router-signature +-----BEGIN SIGNATURE----- +hOYeA7iOxGoiCwpU/1Hdf0xdgO31Mb9YFShO6aXk/R8QxclKJ06snXIecMFZ2XHg +pe/lM4N7dbEZ28qg7rzXKm6fPSpPHUnV3GHgHC8wWHq3f2rklAhkwD3p3Mlq3Es8 +6f+I3PBJo1U2UhbH9uc12L54Qie0IGLfiBAzzuvI23E= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router exit3 127.10.0.13 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+AbXlWS6tXpTxSO/KCdz28Lz6U0jgfQFo1QNfVMQ/DnXuAQAgBABaK/24 +56aVwXNFjWBl8E6ltD5omfo0bRn8gCtkUJ1J2YyZasb3ryGGwwLFiEflqtK6Bf3V +sYuCxBQb16TxZLfWeiwKFu4pfuDHUFasup/B3urtF9cJZCF/UM8NNp4Bdgs= +-----END ED25519 CERT----- +master-key-ed25519 Wiv9uOemlcFzRY1gZfBOpbQ+aJn6NG0Z/IArZFCdSdk +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:43 +fingerprint FC26 4325 EA99 D597 FF94 DA88 379D ABB6 4304 DD9D +uptime 0 +bandwidth 1073741824 1073741824 0 +extra-info-digest 86D5B2698E3D584ED19D105E8EB97AA49D292DDF qf2hb8m/AaKT4ndwcktnJ0dK2R7TNSsCJetcda3yY+c +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBALtzPgl+kve3sBBYNp0e/bnGw0G31AJUws2lM92LgUzF3hkgZpypmeMd +U0Rvz281F4rn0y59qLhedYyD7+kEwCHA21Byfd29WpuKX6r+LFHZ+FaZHiICaztV ++5uoeEyyDEZr6jSjrTwyGatX54csEmUwLw6Ia5HMcBdGZi3QnLsJAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAKyRIonwgQGbe6FeJ9fzLqtqTS8rne1P8IBT0Wu4yMGB39pNL6b3btNi +eAVPJZdgveajvEPkKzwamyh73faKr8oA+n78cJvvU4cXA3n+GC0b9h+mP9zG2Uh3 +hX7coSeevqik7oHePsk3BMkl5GamB5DJ9LpSeHS8v4/JxKBw8KzLAgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +XEMZfBCCmvqeI+j0qr6iKQ4imw94JsVbOylGogA6J1LHbkxbWG1GNKgoY7izA0af +kirZ6GyfDoizPnbq1pxIRen7IlC8sVjFHr1zwZuDOji6KeoNIkJhbjR1uQDGvYJV +zSEwdaAiKcTn2o+E7w4P+4Ow5GbJxuF58bLcN0HEW6o= +-----END CROSSCERT----- +ntor-onion-key-crosscert 0 +-----BEGIN ED25519 CERT----- +AQoABphOAVor/bjnppXBc0WNYGXwTqW0PmiZ+jRtGfyAK2RQnUnZANZO8CJQHdFA +CofZl/tAT2SqZlCc8tXP4flJnTOGBWUkpGqAls3EUzh/XjYXq3NTxGa0GFnLMFkt +DQDaLWNZSAI= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key wGtHyhAKHplfro/I48dUQFE4z1+PQMIOcb+ayebBnjo= +accept *:* +ipv6-policy accept 1-65535 +tunnelled-dir-server +router-sig-ed25519 uOt1IE1eWnJOemIjb9/1CJYBouz5AOikiAOu5aD2SzhddXnrGSRK5A31kAMGQlWrnatLZW+Kp3Topg3T0opGDg +router-signature +-----BEGIN SIGNATURE----- +LWOXcZ74kP67C7AOGAwpk9khWpfPrnCo3ilhxYKuMJPytwWNyZ/L7GM96ITeoHL0 +8bflNXwPCaUEOub92PwCE1+JOYYWJBxGzpiUmdLhVAnRvv6enzynZz3pDxUCKcCX +awyzyVe8WISV5uOcCwD/YsktCxHr+8Shwx68G73Qrjs= +-----END SIGNATURE----- +@downloaded-at 2019-03-25 13:03:07 +@source "127.10.0.8" +router auth1 127.10.0.1 2002 0 2003 +identity-ed25519 +-----BEGIN ED25519 CERT----- +AQQABph+AY41rR2Kk/cLvfjTAzH5dEEae7qehhEdvrbO55gIatkVAQAgBADAuCVI +TD38Md+k2uWuqNVFKcYtVZ+ebS9jGSvIfhpPpE1Fcj2eWCNWJFOu7O1tvq9OVSe+ +l6BaqQv3Ex83s9B99TeJQ4t9mUW2r4F/2ExBeSDVKEbkLcI8L84O4iXktw8= +-----END ED25519 CERT----- +master-key-ed25519 wLglSEw9/DHfpNrlrqjVRSnGLVWfnm0vYxkryH4aT6Q +platform Tor 0.3.5.8 on Linux +proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2 +published 2019-03-25 13:02:47 +fingerprint AA45 C130 25C0 37F0 56E7 3416 9891 878E D088 0231 +uptime 5 +bandwidth 1073741824 1073741824 0 +extra-info-digest 377774BD8BF88A7A5576D43E5287CBBDE7735EF1 jm5TVka0dIzTfQGqnEp1AUZiKEHkpN7fqMBTcK240CI +caches-extra-info +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAM82kmbl7IWKNfwbEfi2lfZgqFPFVPBl7MjufSMOlDnOad0tXbbMcHY5 +jVQVL7ku2PM8SbbDvWZprbPqY9LbWoM/H3QTPgYATd+RQ+eoIuaABBTE10bsx4VC +2WjsztC4xNStZlhuBBVMYNaf8ctHGKTSSCl12PjZUIjnXoBqPp43AgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAKnUlZwRSWLcmzgq0nGkHMkKGHwkH7R5jWKNQpdW8pLEWGEz+gIZrc5Q +OcFrFA1I/G+9rYjVTjbL1ZfgfWmz304xlhcqsNkTkCZZ0BdKBss4pvSiL0LASHNF +1ykYWj9ZTMhQHWlenqnaauxQY70gUpteltHs3eI+BpQznzqt51GxAgMBAAE= +-----END RSA PUBLIC KEY----- +onion-key-crosscert +-----BEGIN CROSSCERT----- +BlWMFdfFy+GUoe4jwNSYOgqFnqyeedhLeOygmEVC2StyJ5CZzK+aq4NFeDrU7r0p +Q5qLSqBfE9FwhRgoj9mbMgLtFtPW9i5f48tEvB10N20Cf6SZSFPW5ugWu5iwJ1/F +lHIcyVRmdII0ZF0n7IhDxmtXn/Y2TILJR4tYZXJczNQ= +-----END CROSSCERT----- +ntor-onion-key-crosscert 1 +-----BEGIN ED25519 CERT----- +AQoABphOAcC4JUhMPfwx36Ta5a6o1UUpxi1Vn55tL2MZK8h+Gk+kAN2aZIbLS+nq +Dik75FTz3g4Q5UqX5RiPfYSxG+yVF4jYl0Tsgzrcm9QHHELG2VGM1IcDKBgzgZTs +6zLGsJUdewE= +-----END ED25519 CERT----- +hidden-service-dir +contact pastly@torproject.org +ntor-onion-key 6Bn3JO/LbYmTcgQcMD/GJgW/A7ogap06F3eZ2d85X1Y= +reject *:* +tunnelled-dir-server +router-sig-ed25519 DBSxvpozeD0u7y+lMI6VFIz8/+kMfgZweQWOUf3zcQm47641RjIIYYY9XCEkqBHkI0sAs0cpU7WRDfz6GevNCw +router-signature +-----BEGIN SIGNATURE----- +IlZxel5EYCHlsafVySxPmDtcjL9cf5JSQYK5IPh8OKRkYcXkaJUyANevbgM1IXo4 +4vCYj9dFJhRKjGEL6U8zckaqeb8NEzPRzI2wxyvNt/IYAPr0GyBk+cYo9KaoAmx5 +75387atFNVisLbSmSWnN3ZbjYPBNLaM+AYHcuitlINc= +-----END SIGNATURE----- diff -Nru sbws-1.0.2/tests/data/.sbws/v3bw/20190325_130909.v3bw sbws-1.1.0/tests/data/.sbws/v3bw/20190325_130909.v3bw --- sbws-1.0.2/tests/data/.sbws/v3bw/20190325_130909.v3bw 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/data/.sbws/v3bw/20190325_130909.v3bw 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,40 @@ +1553519123 +version=1.2.0 +destinations_countries=ZZ +earliest_bandwidth=2019-03-25T13:03:19 +file_created=2019-03-25T13:09:09 +generator_started=2019-03-25T13:03:06 +latest_bandwidth=2019-03-25T13:05:23 +minimum_number_eligible_relays=9 +minimum_percent_eligible_relays=60 +number_consensus_relays=15 +number_eligible_relays=0 +percent_eligible_relays=0 +recent_consensus_count=1 +recent_measurement_attempt_count=15 +recent_measurement_failure_count=0 +recent_measurements_excluded_error_count=11 +recent_measurements_excluded_few_count=4 +recent_measurements_excluded_near_count=0 +recent_measurements_excluded_old_count=0 +recent_priority_list_count=1 +recent_priority_relay_count=15 +scanner_country=ZZ +software=sbws +software_version=1.1.0-dev0 +===== +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=0 master_key_ed25519=cj7V+PYPJvSANsvBOjZSiCvXuGHXFrpSnPqC46I6DgU nick=relay7 node_id=$E894C65997F8EC96558B554176EEEA39C6A43EF6 relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_few_count=1 relay_recent_priority_list_count=1 success=1 time=2019-03-25T13:05:23 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=1 master_key_ed25519=Wiv9uOemlcFzRY1gZfBOpbQ+aJn6NG0Z/IArZFCdSdk nick=exit3 node_id=$FC264325EA99D597FF94DA88379DABB64304DD9D relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_error_count=1 relay_recent_priority_list_count=1 success=0 time=2019-03-25T13:04:43 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=0 master_key_ed25519=2fXiF4T993i+vVwZZEQ4fYee+N8OThzCGeacvnVaNbo nick=relay1mbyteMAB node_id=$117A456C911114076BEB4E757AC48B16CC0CCC5F relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_few_count=1 relay_recent_priority_list_count=1 success=1 time=2019-03-25T13:03:42 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=1 master_key_ed25519=NQspfAK/xkywFHV5LsQ5lLi2BmTKx2Imu4jacJ0d9os nick=relay4 node_id=$4D664E247E530CA5CD5176B8C1A6DABC9531F0B0 relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_error_count=1 relay_recent_priority_list_count=1 success=0 time=2019-03-25T13:04:02 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=1 master_key_ed25519=5O+uCpFoBzsey33+Zzlgyy18/McmV1mpPJZ+DrZMKRc nick=relay5 node_id=$32B7178F7201F76411A99D3552F340D3597D5629 relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_error_count=1 relay_recent_priority_list_count=1 success=0 time=2019-03-25T13:03:39 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=1 master_key_ed25519=uPz8ZZNm2Gcra7BauJP5PH+7uANRraYpCj7NFtp1KdM nick=auth2 node_id=$E7B3C9A0040D628DAC88B0251AE6334D28E8F531 relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_error_count=1 relay_recent_priority_list_count=1 success=0 time=2019-03-25T13:04:22 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=1 master_key_ed25519=r29RWlFMIdU5GUvKsWGdhQIKjIpUzqgw0yNx/7IpPFM nick=auth3 node_id=$35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_error_count=1 relay_recent_priority_list_count=1 success=0 time=2019-03-25T13:03:52 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=0 master_key_ed25519=/PrTbpen3BrKNgiNRhAa93JQtnrT3LJX3Ka1+jvbWj4 nick=relay3 node_id=$693F73187624BE760AAD2A12C5ED89DB1DE044F5 relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_few_count=1 relay_recent_priority_list_count=1 success=1 time=2019-03-25T13:04:12 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=0 master_key_ed25519=C506YEdasDDQqidu4G2VLMFOwaqMX28BYkuxr1+wI9o nick=exit1 node_id=$270A861ABED22EC2B625198BCCD7B2B9DBFFC93C relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_few_count=1 relay_recent_priority_list_count=1 success=1 time=2019-03-25T13:04:47 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=1 master_key_ed25519=gnREYdkUN0uYI3zF8oB+Wwm4MhM6ETPX1hEyt2m2a+Y nick=relay6 node_id=$C7C5094677013F5BC124183C71A482D0156CDCFE relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_error_count=1 relay_recent_priority_list_count=1 success=0 time=2019-03-25T13:05:07 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=1 master_key_ed25519=eR0HnYlzpOEGwxuFjZkGJZ6pu2eV1i6fd9lhF4UOMno nick=relay1mbyteRBR node_id=$934E06F38A391CB71DF83ECDE05DFF5CDE3AC49D relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_error_count=1 relay_recent_priority_list_count=1 success=0 time=2019-03-25T13:04:32 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=1 master_key_ed25519=iriD8sIKS25WGc6mLesQ2okT1Tcn81AuqnEbItJeuvY nick=exit2 node_id=$C0606B414423F9A2BBA2679B440056E3B07FEC85 relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_error_count=1 relay_recent_priority_list_count=1 success=0 time=2019-03-25T13:03:19 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=1 master_key_ed25519=wLglSEw9/DHfpNrlrqjVRSnGLVWfnm0vYxkryH4aT6Q nick=auth1 node_id=$AA45C13025C037F056E734169891878ED0880231 relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_error_count=1 relay_recent_priority_list_count=1 success=0 time=2019-03-25T13:04:12 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=1 master_key_ed25519=Pymu1Z1eZRWgkE42xzDCYFLVKNtY743GKZzt6Im0OUw nick=relay2 node_id=$8E687E91DCAB967F6E4EE8E46E66F6AD05C7C625 relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_error_count=1 relay_recent_priority_list_count=1 success=0 time=2019-03-25T13:04:57 under_min_report=1 unmeasured=1 vote=0 +bw=1 error_circ=0 error_destination=0 error_misc=0 error_second_relay=0 error_stream=1 master_key_ed25519=zOZX+yyD7EN1W/Y2wgjuvObpFOOWK+LZIWlKW0AOcIE nick=relay1 node_id=$2ABFBACE61167A1019A56CB35B2E3362B97D8570 relay_in_recent_consensus_count=1 relay_recent_measurement_attempt_count=1 relay_recent_measurements_excluded_error_count=1 relay_recent_priority_list_count=1 success=0 time=2019-03-25T13:03:29 under_min_report=1 unmeasured=1 vote=0 diff -Nru sbws-1.0.2/tests/deb.torproject.org.asc sbws-1.1.0/tests/deb.torproject.org.asc --- sbws-1.0.2/tests/deb.torproject.org.asc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/deb.torproject.org.asc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,708 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +lrnEWhhzYwNgHYp1PqRava5SGh1msRvscPrRTbXUw8Zps2o9qQrGFM/6kwDd9YkC +GwQQAQIABgUCUVSNVAAKCRB+fTNcWi1ewX4xD/d0R2OHFLo42KJPsIc9Wz3AMO7m +fpbCmSXcxoM+Cyd9/GT2qgAt9hgItv3iqg9dj+AbjPNUKfpGG4Q4D/x/tb018C3F +4U1PLC/PQ2lYX0csvuv3Gp5MuNpCuHS5bW4kLyOpRZh1JrqniL8K1Mp8cdBhMf6H ++ZckQuXShGHwOhGyBMu3X7biXikSvdgQmbDQMtaDbxuYZ+JGXF0uacPVnlAUwW1F +55IIhmUHIV7t+poYo/8M0HJ/lB9y5auamrJT4acsPWS+fYHAjfGfpSE7T7QWuiIK +J2EmpVa5hpGhzII9ahF0wtHTKkF7d7RYV1p1UUA5nu8QFTope8fyERJDZg88ICt+ +TpXJ7+PJ9THcXgNI+papKy2wKHPfly6B+071BA4n0UX0tV7zqWk9axoN+nyUL97/ +k572kLTbxahrBEYXphdNeqqXHa/udWpTYaKwSGYmIohTSIqBZh7Xa/rhLsx2UfgR +5B0WW34E8cTzuiZz//////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +////////////////iQIcBBABAgAGBQJLaRPhAAoJEMXpfCtjn2pmYaYP/j/TT5PP +K6kZxLg1Qx6HZZAOYRtHdGIub5Ffa8NO8o2LreO+GlHdxYyRajRKIlvunRWzcumK +qmD4a1y7Z3yZeSwFCVMzANmki7W7l/nKtfAwr+WZlOA1upGTloub1+0JEAk0yz9N +1ZXA9xruh8qH7HgTIBOM6BF3ZmUmZj5zsoGpBS8wvcPg9V3ytoHGkyowCSXVvNGm +OenlHsxQyi4TsPmMyCtf2Xnjk0uC3iE7U6uSev4Z8B6yXYwKV/NL9lic1VaMu5UG +8QD7JSR2XWFRQgctk8pO5GHXXVcWAnHWK9HvAPhnxv7UCRsb2dzuJzq3s0r9F5pY +S2ea4wp/DOn4PzSlF7D7V4mnPg0CW6+UcEOUnO25z1bAssKnrTngPsb9y9sIveK4 +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 +HCbxIBS9P7zB3sXmQT57jusVSUdQtfJwiQIcBBABAgAGBQJQezFyAAoJEFOcQ2uC +5Av326UQALBzrx914us/lT+hEnfz5aRDE7TwOhrt2ymPVzLvreRcaXOnbvG9eVz3 +FYwSQtl4UbprP6wjdi9bourU9ljNBEuyOAwoM0MwMwHnFHeDrmVFbgop3SkKzn8J +HGzaEM+Tq6WKHYTXY3/KrCBdOy1sQPNeZoF7/rq4Z20CcrQaKdd0T7nAEy7TLQIX +EnKCQKa2j+E55i584dIshxVWvNuwsfeZ649f2FTGM3hEg527BZ4eLQhZQLHkjIY+ +0w0EB9f4AhViZfutakQf5uqV9oRlgmHmQsN5vMKryC1G15HO9HPSMJf9mvtJm7U+ +ySNE354wt2Q2CwX1NdDLa8UUzlpGgR6cd4PmAyVrykEWdtk/4ADic+tu4pTJVx92 +ssgiBAQoi/GMp61KPcxXU9O4flg0HDYjerGuCau/5iUKWaLL9VBe3f////////// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////iQIcBBABAgAG +BQJRcGlBAAoJELlvIwCtEcvuoWwP/ReLzhFKWlc/F35MvNyO1usz+qvs+SrlAtwa +Ncv3Dd9ih0mw+bH+U+PVVgXlk1g0NY9hNNRLxt2mUc+mg9ttN+ha0RkqUYsYjg1W +j9bDuR0a+3DhtuS9hhEjWrBBT3UbTcWT5lxKkUgy4Sj+Dh0N78spHo2orUN3qRw3 +VkHY4hWcxAvlXreuEv6J7Ik4uZ+8MMgJFld4oVhMmnWOrMwt10D58URvZsGypI+d +K0p2JSue5yfBWkSMpFsJ8z2cCOBMAPQq9S63mhXZiORrxJS4pzJ87wcYG/H3R1pq +F6I/49tWBlyZwiwOYs0fFEJc9idF/hSzen/qDDQpvy4gNF48if7SGEtOBu1vEGqW +KvNsataNcjYgj4BZhDlMHgAxWn0G7VNRVsx1D6nzOzEAlFa/PQgQfCXScJXRV72u +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 +=Eq/6 +-----END PGP PUBLIC KEY BLOCK----- diff -Nru sbws-1.0.2/tests/integration/conftest.py sbws-1.1.0/tests/integration/conftest.py --- sbws-1.0.2/tests/integration/conftest.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/integration/conftest.py 2019-03-29 13:47:26.000000000 +0000 @@ -72,6 +72,9 @@ conf['tor']['run_dpath'] = os.path.join(sbwshome_dir, 'tor', 'run') conf['destinations']['foo'] = 'on' conf['destinations.foo'] = {} + # The test server is not using TLS. Ideally it should also support TLS + # If the url would start with https but the request is not using TLS, + # the request would hang. conf['destinations.foo']['url'] = 'http://127.0.0.1:28888/sbws.bin' conf['tor']['extra_lines'] = """ # noqa: E501 DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 diff -Nru sbws-1.0.2/tests/integration/lib/test_circuitbuilder.py sbws-1.1.0/tests/integration/lib/test_circuitbuilder.py --- sbws-1.0.2/tests/integration/lib/test_circuitbuilder.py 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/lib/test_circuitbuilder.py 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,19 @@ +"""Integration tests for circutibuilder.py""" + + +def test_build_circuit(cb): + # Path is empty + path = [] + circuit_id, _ = cb.build_circuit(path) + assert not circuit_id + # Valid path, not valid exit + path = ['117A456C911114076BEB4E757AC48B16CC0CCC5F', + '270A861ABED22EC2B625198BCCD7B2B9DBFFC93A'] + circuit_id, _ = cb.build_circuit(path) + assert not circuit_id + # Valid path and relays + # path with relay1mbyteMAB and exit1 + path = ['117A456C911114076BEB4E757AC48B16CC0CCC5F', + '270A861ABED22EC2B625198BCCD7B2B9DBFFC93C'] + circuit_id, _ = cb.build_circuit(path) + assert circuit_id diff -Nru sbws-1.0.2/tests/integration/lib/test_destination.py sbws-1.1.0/tests/integration/lib/test_destination.py --- sbws-1.0.2/tests/integration/lib/test_destination.py 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/lib/test_destination.py 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,95 @@ +"""Integration tests for destination.py""" +import sbws.util.requests as requests_utils +from sbws.lib.destination import (DestinationList, Destination, + connect_to_destination_over_circuit) + + +def test_destination_list_no_usability_test_success( + conf, persistent_launch_tor, cb, rl + ): + # In a future refactor, if DestionationList is not initialized with the + # controller, this test should be an unit test. + destination_list, error_msg = DestinationList.from_config( + conf, cb, rl, persistent_launch_tor + ) + # Because there's only 1 destination in conftest, random should return + # the same one. + assert destination_list.next() == \ + destination_list._all_dests[0] + + +def test_connect_to_destination_over_circuit_success(persistent_launch_tor, + dests, cb, rl): + destination = dests.next() + session = requests_utils.make_session(persistent_launch_tor, 10) + # Choose a relay that is not an exit + relay = [r for r in rl.relays + if r.nickname == 'relay1mbyteMAB'][0] + # Choose an exit, for this test it does not matter the bandwidth + helper = rl.exits_not_bad_allowing_port(destination.port)[0] + circuit_path = [relay.fingerprint, helper.fingerprint] + # build a circuit + circuit_id, _ = cb.build_circuit(circuit_path) + # Perform "usability test" + is_usable, response = connect_to_destination_over_circuit( + destination, circuit_id, session, persistent_launch_tor, 1024) + assert is_usable is True + assert 'content_length' in response + assert destination.is_functional() + + +def test_connect_to_destination_over_circuit_fail(persistent_launch_tor, + dests, cb, rl): + bad_destination = Destination('https://example.example', 1024, False) + session = requests_utils.make_session(persistent_launch_tor, 10) + # Choose a relay that is not an exit + relay = [r for r in rl.relays + if r.nickname == 'relay1mbyteMAB'][0] + # Choose an exit, for this test it does not matter the bandwidth + helper = rl.exits_not_bad_allowing_port(bad_destination.port)[0] + circuit_path = [relay.fingerprint, helper.fingerprint] + # Build a circuit. + circuit_id, _ = cb.build_circuit(circuit_path) + # Perform "usability test" + is_usable, response = connect_to_destination_over_circuit( + bad_destination, circuit_id, session, persistent_launch_tor, 1024) + assert is_usable is False + + # because it is the first time it fails, failures aren't count + assert bad_destination.is_functional() + + # fail three times in a row + is_usable, response = connect_to_destination_over_circuit( + bad_destination, circuit_id, session, persistent_launch_tor, 1024) + is_usable, response = connect_to_destination_over_circuit( + bad_destination, circuit_id, session, persistent_launch_tor, 1024) + assert not bad_destination.is_functional() + + +def test_functional_destinations(conf, cb, rl, persistent_launch_tor): + good_destination = Destination('https://127.0.0.1:28888', 1024, False) + bad_destination = Destination('https://example.example', 1024, False) + + session = requests_utils.make_session(persistent_launch_tor, 10) + # Choose a relay that is not an exit + relay = [r for r in rl.relays + if r.nickname == 'relay1mbyteMAB'][0] + # Choose an exit, for this test it does not matter the bandwidth + helper = rl.exits_not_bad_allowing_port(bad_destination.port)[0] + circuit_path = [relay.fingerprint, helper.fingerprint] + # Build a circuit. + circuit_id, _ = cb.build_circuit(circuit_path) + + # fail three times in a row + is_usable, response = connect_to_destination_over_circuit( + bad_destination, circuit_id, session, persistent_launch_tor, 1024) + is_usable, response = connect_to_destination_over_circuit( + bad_destination, circuit_id, session, persistent_launch_tor, 1024) + is_usable, response = connect_to_destination_over_circuit( + bad_destination, circuit_id, session, persistent_launch_tor, 1024) + + destination_list = DestinationList( + conf, [good_destination, bad_destination], cb, rl, + persistent_launch_tor) + functional_destinations = destination_list.functional_destinations + assert [good_destination] == functional_destinations diff -Nru sbws-1.0.2/tests/integration/lib/test_relaylist.py sbws-1.1.0/tests/integration/lib/test_relaylist.py --- sbws-1.0.2/tests/integration/lib/test_relaylist.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/integration/lib/test_relaylist.py 2019-03-29 13:47:26.000000000 +0000 @@ -11,7 +11,12 @@ assert 'Authority' in relay.flags assert not relay.exit_policy or not relay.exit_policy.is_exiting_allowed() assert relay.average_bandwidth == 1073741824 - assert relay.bandwidth == 0 + assert relay.consensus_bandwidth == 0 assert relay.address == '127.10.0.1' assert relay.master_key_ed25519 == \ 'wLglSEw9/DHfpNrlrqjVRSnGLVWfnm0vYxkryH4aT6Q' + + +def test_relay_list_last_consensus_timestamp(rl): + assert rl.last_consensus_timestamp == \ + rl._relays[0].last_consensus_timestamp diff -Nru sbws-1.0.2/tests/integration/lib/test_relayprioritizer.py sbws-1.1.0/tests/integration/lib/test_relayprioritizer.py --- sbws-1.0.2/tests/integration/lib/test_relayprioritizer.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/integration/lib/test_relayprioritizer.py 2019-03-29 13:47:26.000000000 +0000 @@ -1,9 +1,10 @@ from sbws.lib.resultdump import ResultDump from sbws.lib.resultdump import ResultSuccess, ResultErrorCircuit from sbws.lib.relayprioritizer import RelayPrioritizer -from threading import Event from unittest.mock import patch +from sbws import settings + def static_time(value): while True: @@ -41,8 +42,7 @@ persistent_launch_tor): now = 1000000 time_mock.side_effect = static_time(now) - end_event = Event() - rd = ResultDump(args, conf, end_event) + rd = ResultDump(args, conf) try: rp = RelayPrioritizer(args, conf, rl, rd) results = [] @@ -65,5 +65,6 @@ pos = i * -1 relay = best_list[pos] assert relay.nickname == nick + assert relay.relay_recent_priority_list_count == 1 finally: - end_event.set() + settings.end_event.set() diff -Nru sbws-1.0.2/tests/integration/net/auth1/fingerprint sbws-1.1.0/tests/integration/net/auth1/fingerprint --- sbws-1.0.2/tests/integration/net/auth1/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth1/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +auth1 AA45C13025C037F056E734169891878ED0880231 diff -Nru sbws-1.0.2/tests/integration/net/auth1/keys/authority_certificate sbws-1.1.0/tests/integration/net/auth1/keys/authority_certificate --- sbws-1.0.2/tests/integration/net/auth1/keys/authority_certificate 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth1/keys/authority_certificate 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,46 @@ +dir-key-certificate-version 3 +dir-address 127.10.0.1:2003 +fingerprint D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD +dir-key-published 2018-06-01 15:55:49 +dir-key-expires 2020-06-01 15:55:49 +dir-identity-key +-----BEGIN RSA PUBLIC KEY----- +MIIBigKCAYEAsIDnUuNB5InQb7ipbXwLpIEtkFowYfkjqFlUVCh9LkMoOzlPcQOd +opYDjCD9DLuzMG+d37Q+50V08z2NHOzlRre8RwXvAyPoDoWtPLvvrHetBScZjYYP +Y1FqUsooD1O9hANN/sTcVYxVIALLYHnxoQMcZciYh4cGriKqxQb3jaRaxVVmH/K4 +PCQ8h+Tlak9t3IXwb9hE4EuwMQVid99Q5ke24Az6hcmAaMia5MILyxybycomHgNK +fj7Awym1oRvZV1ZH8SVmP3si9Y8NFT1+l+HhwmCX7001uwjIhRiScOAvLU6NJQJN +Y8KAWzMwsfFu347lIwU7+tJnF4GjDGvUMYS0Jl/FGPOsgTxfpFfOa+l3DzZ3PUK/ +4looiS3K+8snObiw18iq0U/J1FfRPQ3te2nsDS+NFFWrTyCjz7LfjnaeIRHu2FFu +782s9SDYIzi43HmWEm4klQB/4H1l4Na/evbl5b+ky99Ar2TI8x4ZBu40wC1gbuG5 +w81nbF51eGYPAgMBAAE= +-----END RSA PUBLIC KEY----- +dir-signing-key +-----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEAs3yPEimVwdNSu+48MPBbqAscL7ETps2gMF/7NFtGK++4GU5TQ4iV +zfjqzhV4n0CcCuQ+UGB6kzr7ZCsd84KI2fVbIZaxtD++Zb+jc8bRTp+Lz27J5FDd +LTKCZ32EiBoTiVME7zfVHHdxS2aa2L2/KqWO5BDDjB7kn0UoN6yE4aWvE1y3iXQl +uZCWXryc6ZfF46355aBqGKb1Ru0fJSrsE/spdiymMfGpOedsa0jgZf3GohkRukUl +lCblFgpSjJalw6V2uWi7g7qC3w8K10YGgBvNNNPuLrwcbrE9IaO2DFRFZokZM2l4 +y91DQfYkQrDGG/hAkX1oN565/eeH6FhltQIDAQAB +-----END RSA PUBLIC KEY----- +dir-key-crosscert +-----BEGIN ID SIGNATURE----- +Ow60/hsr5EQMOtKZEbr/MncxzQxbDbSy+pTJrFCYXamwKgmN5CX0ZaPnWbVJsmSe +fLV8izHtx75x80oFXt2cqbY792uLWp9O/8S2YhvGSOgRPJ0edUZtbfkxgA0ConYa +FkikHND/pulK2XgaX1oC7ZLqchA6fTLuca9xFA34n/1/yEr5PyLwEicF8GrR8/ir +LnRRv6aiQSf6ZJetctep31Bzrs9WYggXK2JCst49hqkJbiiGVrpmzlRpo9MajP0z +IwpDAlzKnuKFRMftBinh42+kgIKyQBgYwyJAzh31wuFY3b8zaImi4dKwzHdzksuz +Uh6XrheAfVDXOEeNxCbFgQ== +-----END ID SIGNATURE----- +dir-key-certification +-----BEGIN SIGNATURE----- +kW6UWcYhRQn60GuUrRtbsJm8CaL42o2ywX121y4tSmGsTdmG/rs0Ve52fJvi2qOW +p93hecTEVLbsF7LFAM8454sW9BsfTQTZ0IDzg8F4wJ187m7EJMhTyO2twwXO+7x/ +fKV/4viHzLLSqLfW8txWeozazwC4B7VyH8WHxKK5/VhpQXgMlVaaTtzfEd67+AuJ +qnCNTxKeWXNGTblRXglL/K7MpP1MrQq0T8W4eEFYGhYT9aMM2wvKeC9YAl1xePdP +/yLH0Jj9iB+FjrSXUDHGyi0QRgSJMiAW2uZkTtODxUyt4i1yx55KKlK6jIgXu972 +O/O+5h6YTqfcvqFZDWaATZsbtX4HKNmF1cLfvX3XFpB3O+YgRaz9bh9yly334UPk +J2gYduqikYu6yYwIdrsPeT7aLjGh829P+BYUgVkbiDnIF/N9n8oQNivyX1qL2sm5 +O/JHog7BT6uuud3M6MuLvfZWqY5BtiYexVQhXBge6mcrjl64IK3T8unDluY4vhah +-----END SIGNATURE----- diff -Nru sbws-1.0.2/tests/integration/net/auth1/keys/authority_identity_key sbws-1.1.0/tests/integration/net/auth1/keys/authority_identity_key --- sbws-1.0.2/tests/integration/net/auth1/keys/authority_identity_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth1/keys/authority_identity_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,41 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +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 +UN35Z33vkMLm8ia2zHY= +-----END ENCRYPTED PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/auth1/keys/authority_signing_key sbws-1.1.0/tests/integration/net/auth1/keys/authority_signing_key --- sbws-1.0.2/tests/integration/net/auth1/keys/authority_signing_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth1/keys/authority_signing_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAs3yPEimVwdNSu+48MPBbqAscL7ETps2gMF/7NFtGK++4GU5T +Q4iVzfjqzhV4n0CcCuQ+UGB6kzr7ZCsd84KI2fVbIZaxtD++Zb+jc8bRTp+Lz27J +5FDdLTKCZ32EiBoTiVME7zfVHHdxS2aa2L2/KqWO5BDDjB7kn0UoN6yE4aWvE1y3 +iXQluZCWXryc6ZfF46355aBqGKb1Ru0fJSrsE/spdiymMfGpOedsa0jgZf3GohkR +ukUllCblFgpSjJalw6V2uWi7g7qC3w8K10YGgBvNNNPuLrwcbrE9IaO2DFRFZokZ +M2l4y91DQfYkQrDGG/hAkX1oN565/eeH6FhltQIDAQABAoIBAQCIU8mPsApRiCNF +HvSU71uOzbGA/+y/O1u6oox4/gLUiGW6wuU3XFz8b15ZXbGatEZRd39lKYKwpZIg +SSect78eXd/0KTOchvYk7mRGIfME1uYojivGwSzdkYm+9XVesv+3xFjxIeU6k/Z3 +oN1oVucGi213CMyedzCLUMn1T5I2mI3pb/xtKnsXRNE5Hik5ve6f1Gni+oXkjbOX +vlcnXjoslmTo1KBcTTUVsNiUqW9SC0NSZeb9rqty/kRZWMJoUeXOuPn/hUn1qpz8 +PIMgmEvafMDdC1Gk05tt23C2TYWwAhMQHl5WOzzJ5KcPzSaoNNAaMmqYpuh2RM+y +T+6gy0RZAoGBAOAMsvT7fEgyfbBtnUHFl73ykNzHcg0hoU1xXFL7iIRgUok9QjVN +M1J9hIYTEvq2NFXlPaDyz8+MzIKhsDADaJzrRglohvB5sm40gGdAlaJ1O3T1WPta +f+RoZxdKt2YEWStgh24+6dA4DajZvA8d/jt4NlYv4eszZidEiqzGIK2XAoGBAM0V +Axy+YNGoxSgW38IJ+K9nb0mTX7g2vm/Xh9BCARuK4iVajWQFeYflkgxClGbrIdaz +iDZJY36wkGXkU5Lr+314oTXfppdruM5EShP5dHl2qCBoel53wrkksIno/mpayB7Q +ypiSTbdiB/v5gseCq4ZwHEgG3AZykcKmRkDoGAiTAoGBAJGxkFngg5QLSVKGJUHU +9PTvHKaFB7RkTxkmA0xvBvpWDLXz4O76UcptltT86FWiUGwe0zGrU9l6jK7R59mB +DfEkyXlqwHz2FkK1313FUMjBBTXsKb3hvtLP1WH/Ez3CzB15+veE79SX7sIDPNhz +v5P6exMWtgmVdfNJDgiGGk/nAoGATQLQ8mBDNzfrIUzFpbXc3vE2hyiCT0ivZoxH +AdtnUxJqLaC9a4vqwS9iEpyF4cf2Iwuz4L7aT965uqltAMUA4XMFBpf/lOjssvhN +4QZRyLLYG6NgIE1Un/W5HXCS6RlCAOBi07xM1spHOUXO6GCXTJqY5Cs/QGSRe8i4 +pPMC42UCgYBrIGQdyUSJCJ1JXG7IDI8VzYrsFsHRbniUnUBvq64GInLdwou8+xDP +fWVfdqQXlLHyzU45fIGt0Y/v/awsZ9WP0T0hL4x1rovDJqJoRKMgmf6i3vp6y+sy +QbnhMPGps/VA0rPAb7HQF3a1xo2q3hGjc1bv5riigttHDAdddWCP4w== +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth1/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth1/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth1/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth1/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth1/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth1/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth1/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth1/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/auth1/keys/secret_id_key sbws-1.1.0/tests/integration/net/auth1/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/auth1/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth1/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQCp1JWcEUli3Js4KtJxpBzJChh8JB+0eY1ijUKXVvKSxFhhM/oC +Ga3OUDnBaxQNSPxvva2I1U42y9WX4H1ps99OMZYXKrDZE5AmWdAXSgbLOKb0oi9C +wEhzRdcpGFo/WUzIUB1pXp6p2mrsUGO9IFKbXpbR7N3iPgaUM586redRsQIDAQAB +AoGBAI1ECZaiQnr7qhmUdSyWKGOKBEfu/Hhwy4x4mDWu22P98s4YIBPzLEH7Na+P +1EQgu+puypSRadfmwartzyRHvKzA0QU3M6j7IW4wKg+hX82gXg6BXkcwhlyyhrs8 +MMfTQgqdJsD1fyDr+HdIpNosDQY+3UwxTEF685uncBzOsEGpAkEA3TCQOe0DbTMT +vU87a+jdB1TzkcqR193Slq+QUPTbHlPp4ZRqiHrHfEFYUTAfMrLgiQ9wjO6+8LiF +wAyal/ItgwJBAMSO0u5IBvuV3EVJjMLCrBmKgyDYNgcJbFtS2TRJrOf3Lcx1fNIZ +5gQkZa3nMveJTz0pyIZy7u95D1kimG1uMbsCQB/iSzf+YVsRXMwSKsegVpQi+8VN +RP6v/BY6knwI/7j8cZ9RzJWF1VGk/b0eSbD+Nf2r2xaFp8J9ZtJLPtGeWE8CQQDB +9ephpoP1edFuMgEMI4wufAefvpu53+ukRqH9vgI4uSrhisJMla/sQHpx3CboLAAc +bAHr4rizaTPjEod13PqHAkEApA1cDrg+iaoOPC1oepafihiI76shg0h4C0Y+vUKM +e4NQO585teKCQYOgv0nacERL+EcANGW6hd0eaRDCPtFxwg== +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/auth1/keys/secret_onion_key sbws-1.1.0/tests/integration/net/auth1/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/auth1/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth1/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDPNpJm5eyFijX8GxH4tpX2YKhTxVTwZezI7n0jDpQ5zmndLV22 +zHB2OY1UFS+5LtjzPEm2w71maa2z6mPS21qDPx90Ez4GAE3fkUPnqCLmgAQUxNdG +7MeFQtlo7M7QuMTUrWZYbgQVTGDWn/HLRxik0kgpddj42VCI516Aaj6eNwIDAQAB +AoGAKHVDSIwEDl7aA4tVivY7eYuN0i1pVDbScLj//eRO1KqUyPaeoZ5xJuQ9z/kD +BGk9OuC+k8zMIj7oB4TwUK87kKKihgIkQacuBa17ERc+xVusgpEJlYKNHXCe3vFm +qPhHfl1h08K7sXDE7bB81vwCabSHqCFO4Yy4KuM/XHRqZ+ECQQDzcL6qNzsJpQRh +wUtXDpQ2oDxExUohZNQ45fkGIJy1zRnxKR9Drr1ikM58xdelte/bFEjfImvj624M +YFGIrtQVAkEA2edafRFHu9kDm7Bp2s4gn0ysdJ+pKmcX1haoJzqFBKOR/CzgFhkU +w1ptBUWR+6A2OigFOIsesS3lQBl6ROVAGwJADXrnw+stBLqzhGo8rsRq5p921uQJ +19GKguARKwqxQsvprNX3GjtZ11RnardV2qI5dusnodt5F/TYciz/Ns9fhQJBANlp +YTq6PBVsEJKNcQCUe64VhVgTWWjqVbkRABOr6r5XKbgvWiISQkor9ey1nlMuZgye +k574B9aGUV8kTH9jMRMCQBUieqme0Q/iISEY52o1TwJfs993FQIFiVg8yiS1ekN9 +HD1qnkmmusdGmEGbYYGpLjpf8nFw+5IVtCOsj+5WOjE= +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth1/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth1/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/auth1/torrc sbws-1.1.0/tests/integration/net/auth1/torrc --- sbws-1.0.2/tests/integration/net/auth1/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth1/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,34 @@ + + DataDirectory auth1 + PidFile auth1/tor.pid + Log notice file auth1/notice.log + ShutdownWaitLength 2 + ExitRelay 0 + AuthoritativeDirectory 1 + V3AuthoritativeDirectory 1 + Address 127.10.0.1 + SocksPort 127.10.0.1:2000 + ControlPort 127.10.0.1:2001 + CookieAuthentication 1 + ORPort 127.10.0.1:2002 + DirPort 127.10.0.1:2003 + Nickname auth1 + ContactInfo pastly@torproject.org + + + TestingV3AuthInitialVotingInterval 5 + V3AuthVotingInterval 10 + TestingV3AuthInitialVoteDelay 2 + V3AuthVoteDelay 2 + TestingV3AuthInitialDistDelay 2 + V3AuthDistDelay 2 + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/auth2/fingerprint sbws-1.1.0/tests/integration/net/auth2/fingerprint --- sbws-1.0.2/tests/integration/net/auth2/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth2/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +auth2 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 diff -Nru sbws-1.0.2/tests/integration/net/auth2/keys/authority_certificate sbws-1.1.0/tests/integration/net/auth2/keys/authority_certificate --- sbws-1.0.2/tests/integration/net/auth2/keys/authority_certificate 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth2/keys/authority_certificate 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,46 @@ +dir-key-certificate-version 3 +dir-address 127.10.0.2:2003 +fingerprint 4EE103A081F400E6622F5461D51782B876BB5C24 +dir-key-published 2018-06-01 15:55:49 +dir-key-expires 2020-06-01 15:55:49 +dir-identity-key +-----BEGIN RSA PUBLIC KEY----- +MIIBigKCAYEAskiPcxMAOY6BdDha1zEhsvxdNxWk+Fuhx8OEpk+Yn8UhT+8vOPci +KWRtrFQte6U2JKxvXkPqoS+r6j063cUW1S+No5LuLm3VZ/lbJPJr8T/4MFodBqJ/ +3JgUycQBiBepCJTH/IsgFjJKwjvVYQPmlRgnI0Eaxeelb/TslRan7C3oSmAQ7fMY +c7UgQWtZQGCtIh3ZjrL2NBneZVebVgYst+kv0GdTFBQ4Yf74CXo5k0Wk8KF7YCwR +BEtGcIHh98jFahS2n0PI5gjBTJDPuuLVBpjqQ+WlfYy2njQagevNZZuTWorewomY +kIcaWLCy69Semjz3CWeOgxXCm7bMQesQbqpKubTRG3CmjO7F2ycA1jP45tHEI95B +wy55S+MQFhWHwj+VdVSenIuVKDqbz0yg02MPwKysYI+6DnY5SDTAAjZ50d/5kcmi +QTnnIBdI26/KUDbZsYFb7BHuayKwSR2CcOfrjhXAhpAe200B49Q5S4jtrSIBUo3a +L7orVFAuYRsbAgMBAAE= +-----END RSA PUBLIC KEY----- +dir-signing-key +-----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEArguSeQ8/hHeR2YXkwxeNJQXcU4ZyxVA/m1RF9eNznpzM22gP/y36 +FV2jgoFzGFcndSG3bD0x7SOvcLzilVAkurQm77+uuX2sBLZJSwwTHunRgCWxuRM6 +amPOfS9/K9G3h4CVSAVOteA43NPU2idMwu8QmfGOkdEfxTk8gtSqqRZiGIRPw8SH +LPp4lpu3P9e9MWUfTBh2Rrok5ELCbPTL3YquHmvMApeoXCi4HZLEMKWQRHqeOrfq +0eFIat26oFeRCBGFyMCKly9KkmsaOgeANmWS44lyIbYViWi6Wd8DBZyJeET2avCS +PTHuTveorCxEIKhn5i+S6x8YBgW9L+psbwIDAQAB +-----END RSA PUBLIC KEY----- +dir-key-crosscert +-----BEGIN ID SIGNATURE----- +YDO98bj/jDxsj1qKmbnprUVsd66tEfCO/4cFR+8djEYlap6uhw1RsaOBIEAEyi6o +B9ZSStq/9WQDb8IQz7MTl0zPtUUnYGcJqmZirKr/bi8xQIbwDTFh5JfEcVGYiHC+ +tapqxXLOWyiOZKJLnLPLnHTzrT+HOncpJAi66vq2KGFWJ6c6QotLSiMgku1JoXif +YP2PCLxLSRR928gFoHy0/WcEEMC/vE40gSrIMQ86SCB4B7MNQ+ENRMMldqw4pJaA +6eGZCyIqININZXshqOXnt5whrAaD9+gFTrbmpX+4fdBmQNfk2ZXnnGC/mw4rCegw +8ivl3kDb++7cv1BBGgUXrA== +-----END ID SIGNATURE----- +dir-key-certification +-----BEGIN SIGNATURE----- +jz0lA4wDbYQ2S8MILRZH869pjWIzH9SrioswiE4mvWbXmttiN865etqS8zWpJYru +Ck2IHEZVGxnZ5CKT/oMjQ72bNCzjHvDBPn4JnlBvnCRL7ph8PZLKtFeryc/3ZU1m +x5uGUjqtusMeoJnzC8aLR4fWZcjcAWjDyyFhFLoV76O/kQWJr7lBJRV8xAMjIRzu +XIo+nzXbHegi6d8RnptfKgqd+uw12jY1pb016Goixgs8dzMDDtbkxOD3Z/8wzUX2 +pP1+FaBZH+ydklA4FoXwNjND6g1nXDME6l+fAklBLjxOjrmAD/UiWe7G5cF3ktD8 +js8aJjuTiIUax3hj7yQzA4i/8MGG6DVIDKoWbsNADPH61JI+lGtuHSeQ8Glz+zJj +rK6Extqacd7YxwDLqvwPbWsoLlHZtAuXEyOE4Q7j8Eb4x9iblPsATMtcKC0wJtqa +Zgbpntd5mUr8CK7P0/idGM+s2MS7lQW33IAAUBsPUi+UbO1LpPk7hg8v1THy8wjk +-----END SIGNATURE----- diff -Nru sbws-1.0.2/tests/integration/net/auth2/keys/authority_identity_key sbws-1.1.0/tests/integration/net/auth2/keys/authority_identity_key --- sbws-1.0.2/tests/integration/net/auth2/keys/authority_identity_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth2/keys/authority_identity_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,41 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +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 +tKmy7xJz3KeNCK8abnE= +-----END ENCRYPTED PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/auth2/keys/authority_signing_key sbws-1.1.0/tests/integration/net/auth2/keys/authority_signing_key --- sbws-1.0.2/tests/integration/net/auth2/keys/authority_signing_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth2/keys/authority_signing_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEArguSeQ8/hHeR2YXkwxeNJQXcU4ZyxVA/m1RF9eNznpzM22gP +/y36FV2jgoFzGFcndSG3bD0x7SOvcLzilVAkurQm77+uuX2sBLZJSwwTHunRgCWx +uRM6amPOfS9/K9G3h4CVSAVOteA43NPU2idMwu8QmfGOkdEfxTk8gtSqqRZiGIRP +w8SHLPp4lpu3P9e9MWUfTBh2Rrok5ELCbPTL3YquHmvMApeoXCi4HZLEMKWQRHqe +Orfq0eFIat26oFeRCBGFyMCKly9KkmsaOgeANmWS44lyIbYViWi6Wd8DBZyJeET2 +avCSPTHuTveorCxEIKhn5i+S6x8YBgW9L+psbwIDAQABAoIBAD71+LrKhVPKXXyv +Lx5UOr8V66pbjNFMQnZSeiyFZ6INXx5P+tl5LGrmQOxJH/XCMYfx9oS1boY2zpSd +/+m74E2aNiusz+xSRhQ+TS1U5OXd9+e/uxcUmaE7Ecu/juWvLBFctqQjJ2IsBuX7 +y5hPdzldekf2Vnjxieiy1+0MOeCImynlBJcp1m3LjwMkXcB5GGcmGknsQMAT+MtX +eIPw8xD3il5ZfsDuHVnUWTPryPamQQls73aKV6oaiZreZ/VqYxIi/aS+gWlhUBZM +CpLIVxWhpoiM8eMY3nCDTT3t7kVOoFNuCnOZpwTuO/mhsvMiD/M/tHBKem3Xf3Im +ifVufgECgYEA5GlLOU0oZwrpZGYoDsEUQBPSJyVVRjnBUGXkaNYuDR9ABuyD7EhY +KK2mqovZl0irNq/suROJnKVG/l06al1gdPZsPh+WrZ06dh2lvCyF8xYQpQK7grgK +L83CGQW2x2ydm2PyhA75Mel3kkORpkqKu3368w00nO19aR4JXU4iD8ECgYEAwxE5 +XP6CaLMrw03qYkFxdRRCvvb25y6RVQp7KSI6YZmuAJUTko9kXDi0YeCf8Q8IN862 +fHX8smsfBZ8m+dYCYKcGa+biLyEPHjuKOlh0yhekZqmyZT2V8G4ECTFPXN0ktJyR +W34sY1KXaET0n6KUwj1aOn64amPjy+ndZ5ILiC8CgYEA4/46SRs8bgaVS1MxPvD5 +jrB6P/ri8LMrxF3ArejElPKRjZe+YzcfUJNtXqUz7+9of7EBQQhAf2z2vWY/rfZB +Ft720tfjJq9fsJENYxSqLCflO2DL3MJp4KeozCNI/Czt8TLF2426rliVsxjhJLUZ +X+dze/K9hrsESM3jmRAprcECgYEAvScwlsp9LoW/RA2D4G9RU5Sn/IGCfj6b0vjs +4MA+GHJrVkk4x3+gdATQdbDZp2lKMMOUFTBcL67MUCk6JD2v5IVSJsOmRMkr3cci +jN1FssomMx8OzDlh8djctQVJzeTrOPAxy6SX6mAmTYfAJiQDWC8iFWLX9OKT/CH0 +/f+SiyUCgYEAx4HyM/v1i+U5j/2Yc9m4ex3P2fvOHsxQvebdrmCc8Gqp8T/552ht +NWQ1tFP9c/QLQIV8y6e7tg7kLuLz9LT+pKEYptseF1JnzatZ9kV23U6R3ys/xkqR +Sh8q+OTI41kWtB0mbg62oAlBR57vdmf9YvuqjsheS3Otj1ppKvfxP/M= +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth2/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth2/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth2/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth2/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth2/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth2/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth2/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth2/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/auth2/keys/secret_id_key sbws-1.1.0/tests/integration/net/auth2/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/auth2/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth2/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQDFphY1WTrmNRGgRjGHP4AwDUYQ+xY85SsKKcC7kJwsgiVppPJq +SS9FCS2YJNMcs/u14IhNKIRtP8VLI/rurk9h2Qnbbb4r4FSf0DBmDW8Qhzag+UUu +aV29EWB/To6JO4cxm6hzpzz+Bl3AJsZN+Luzgmk6ZfAy7dA2jrvAml8s1QIDAQAB +AoGAASMQc6Qcw6BAhrSAqm+CFESvR3+6RAVpdviAhFOzoknpXy80ucnL/NCts3da +w4qBgy1Ue2UlRd+dCe6eDetwx3O5lEhzj6WZbnz9f+KuXOKP16nisbQSFH+UTFnn +0PMHYJIFbrPzqptEV9hlKcNljpTTua6Dl1N+bijENZVTds0CQQDuHCBKYJgOk1ma +rmZEYt3VMLiUfpAwtVdmHXYV0xPWpkWYqHbZrit6kLi1fwvqc6I4C2pebFhFF71B +m0ckFdGfAkEA1H+4ZrJkLV9uqk/Vj9bZHNF3212lK+cpdx0Og/4agdsVKbQfLF+W +1dnazZ9tIm93kMdl75Ave2PUKOMfdu71CwJASV2PMXubvnn2VOvBhPa1hTey18Y7 +ZVaqrEMNnGHiLfQjY4scu6unp+cpknqn0EB6zivuRVO6yucqoClY/Sxf3wJAI0Y8 +Vxff10RTO4RbIDB7A/Sln82QwJPz5yj0ouaIgevbPHwA/L21bTRnxOBlHmVAhNEE +09qsdyAgNNVXAMgz9wJAWB8pJffmHKXsmWaIU2Sxlc5JKZx23kMW9PkppW/EyGTR +txQaVsLG8xy0ZaSp3VuvbYCbQ0LQ/IGQtqTtsQNn2g== +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/auth2/keys/secret_onion_key sbws-1.1.0/tests/integration/net/auth2/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/auth2/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth2/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC/EEbOor8Vf3UACJcpmDdvrSil/hosbD1xRQ0lRbquChuOPIT6 +IfYq3/6rncrIqvQGF8nOMIYZK61WST97VvgnPYPIVwwlcURa/SO7+PHFNhd4iDl3 +5lqTvQm1zuvX6HONwFJJqxPiQUsPHm2K0aQcMKlAojzD2pFZDu4+8+DTvwIDAQAB +AoGABoxCQPam4c+6bMPemlhTHgqrJ/UrZHZZLLaXGr3nBevmwdslG4VqcFG44pzH +xFTHhCXwjpZvIhGzIQ/lkw6J9o8l9BD6+69tySp89OiCVHZTXRjNfHIzchprUR8/ +gVBPEgKapI/5Tm/1Dy2DYcoTA5e5mPf4N9bcoumGWtSZdJECQQDguVJL20iTBu9h +I20PRrydSQXQ7KVc4nZk5KrndsI4243KclVef2LtAebedSHI8tdpnScAB4vFYH/y +VneaXC6LAkEA2aesMSQFZe/Yix49a7FORzdgO3Nu9CrNHhSug/61gfUzD49Z5j2Y +8d0zz4WY97tWPwwFZPqnqRVKY8zaY75qHQJAY7e4XfLDJILKTCdlKIcyAPU7QHcR +9cKD2MdjUuxuyL4cD/5mBVWvdw4F/SaqxDmF/tZ+TNQMYJwuikF2Nye6ewJBAJ+i +pd1o4Pix4C2w4TWSVqpPCCqycEdu+QA+BP1UnEWT1H5uEm6W8Q07wxtKjjXsVcZq +B67XVI7jyarG/co4R1UCQDOcvt35RjAPGFPpRfBfKqCrc1kBiFLrawcgPKB28BIW +TW3iKZgDyGHROyluwoOGRZIKUGhzCxsd/whQ6SXWqas= +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth2/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth2/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/auth2/torrc sbws-1.1.0/tests/integration/net/auth2/torrc --- sbws-1.0.2/tests/integration/net/auth2/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth2/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,34 @@ + + DataDirectory auth2 + PidFile auth2/tor.pid + Log notice file auth2/notice.log + ShutdownWaitLength 2 + ExitRelay 0 + AuthoritativeDirectory 1 + V3AuthoritativeDirectory 1 + Address 127.10.0.2 + SocksPort 127.10.0.2:2000 + ControlPort 127.10.0.2:2001 + CookieAuthentication 1 + ORPort 127.10.0.2:2002 + DirPort 127.10.0.2:2003 + Nickname auth2 + ContactInfo pastly@torproject.org + + + TestingV3AuthInitialVotingInterval 5 + V3AuthVotingInterval 10 + TestingV3AuthInitialVoteDelay 2 + V3AuthVoteDelay 2 + TestingV3AuthInitialDistDelay 2 + V3AuthDistDelay 2 + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/auth3/fingerprint sbws-1.1.0/tests/integration/net/auth3/fingerprint --- sbws-1.0.2/tests/integration/net/auth3/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth3/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +auth3 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C diff -Nru sbws-1.0.2/tests/integration/net/auth3/keys/authority_certificate sbws-1.1.0/tests/integration/net/auth3/keys/authority_certificate --- sbws-1.0.2/tests/integration/net/auth3/keys/authority_certificate 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth3/keys/authority_certificate 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,46 @@ +dir-key-certificate-version 3 +dir-address 127.10.0.3:2003 +fingerprint 8B85069C7FC0593801E6491A34100264FCE28980 +dir-key-published 2018-06-01 15:55:49 +dir-key-expires 2020-06-01 15:55:49 +dir-identity-key +-----BEGIN RSA PUBLIC KEY----- +MIIBigKCAYEAzt9HthC1wszRQ6CO3yzceu2ARDOR2FrHRohIbRDvNxMamz+T/CUb +63lcgY/ZAkun2DprrVGn69WvyhjpX6zwpLYVxA7pgzmTCAUuBhl/c9ZyWhe4yZqt +qc3eg0yDac1U+oTe8MAEqAZvLmB6uymc1xnoGAoPYmkk+V0rzaDib5sKNGklsLn9 +KerD8/h+OaTygj9O6L0aNoaxcCY42+3w43tn70rk85SBhN+EVnpvGewHCxyiP6ev +0jvRFg6GU6RrMFaN/YokYobnbHDIvaBWaYjRe9fWPBUpVWLdNvUDTOWZqeTlkVvk +K09OJ32mbKUPdKRIIDV/OGnK4WR3tQX4oW9vVUHjKAHajv/iGYuDEoui4G9Ovw4j +o0fosxETFvH9B/Qg7UT7rxOkVwX4P87sIV0Z1TwH4S7mIhTwhICD/MLm0m58j8MM +Qd5Rt+4QFEVYY8vswV7XiN3xHwsiMcR7MhhPLcaTG57rc1pAVVRJLAGf7NNptIED +dV9ha3ZFOClRAgMBAAE= +-----END RSA PUBLIC KEY----- +dir-signing-key +-----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEAsFTr1o5iNvK0yqQRfNcUBk81BLNKI1Uv9uCy6mDdneYwKZqVeB5w +5NQe8qFSGWzLWWfi7ZdQaOPHRUJWqdtkuR6AFEh9Ws+wSU17Jd1uMKfVFQL0+4q9 +tt52yoCE2tJ2GFwcouirUGXCXFQbfCrmUWxajSgGpDGNvjf24td8kq/O53cl4gOp +d1Ubl2BsLxALUswRsurJqLt1r+WNKEV9gBXWavyWxZcOwBWUhExdWWVSlM2O2hYs +Dq/VV8MJHugHHAju0d7JYhCy8L4K/AsZJ9KDgAyXw7L7ur7vqIzX/EjqtOjPk+rK +dpe9d3SbxdHRN0hmwqx7n4zGDt0AD4sQ1QIDAQAB +-----END RSA PUBLIC KEY----- +dir-key-crosscert +-----BEGIN ID SIGNATURE----- +dE7ZA/wt346dO3uGUMM3CGQjm4YbPO7OWqmTAk0ohIDgUWT3At2fsitT+4ayZLGB +EFL4yeeeaT3wYNb2kCW/JLI9x1bFXs+wO4CPLe70ynbk2uB9KIrOy4hH9EW7PpR5 +Ow1N/AzUm0CQQvLstLNbUEBl7rfS9edU4Mg1GZUQTM+CPWxUIlaTtAT858Nbor1q +vJE7B5nVmozpla//0rE9Vr8Yu7JGOnCYdO002uo92AkwuHiDfBUVmzhJqaV75hHC +3zrJIDdHtYpI9ctuUNanWjVksuCkN1SUWmFYJPDWSChXxll8EZTVtk3Z4G7Ddgcl +4WZ8tb5PbzFypt2DSbeqKQ== +-----END ID SIGNATURE----- +dir-key-certification +-----BEGIN SIGNATURE----- +ZQ5ZVR5cgYuDbg182OQRIri7Md/L+DIeCEf0ZuApMFytgTZOEExsmccJlIibrfzB +hFAvBuxN+WDATEawg4FltyXrgB8M3FDrYufBQua7v9ghtapxsxL4O+G02Rq6RTZ1 +NIV3eE034UOmZ33Jd6k1EcZykeu/wF3ThTtg1pM75g56zDXk9xn+MyRb1ja7Oor4 +mzTBncgjo7YBnmNN/eIpph445NiduC3BdrLEsrCJA5F1iCO6ubeKx9GLAMIGb+W/ +qIXiuUiCmgp+iPQoF1rYRRaUgfo9zysm7ikjiSe6rSifLPth06vGWvUgpG0xm2jr +96igZ9MsfF1loaMUV7g9MhTzDFRk+Fw/KrAPkXgisje3RNtaI3UiCqbs/Pq0x7nC +77S6B7dd9RkhtWmpGFnfqX5I/l+766xAGXMO9IPlRp0L6EcOhIA+7N93mGf9dCKv +85741ULH0pZrdglPkXXsNGv7EO01uKuSrTkjK8Z/7hM/mxib19O3CTI3eX1WPQPr +-----END SIGNATURE----- diff -Nru sbws-1.0.2/tests/integration/net/auth3/keys/authority_identity_key sbws-1.1.0/tests/integration/net/auth3/keys/authority_identity_key --- sbws-1.0.2/tests/integration/net/auth3/keys/authority_identity_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth3/keys/authority_identity_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,41 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +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 +T5zlwSssCEGZ7wceiVs= +-----END ENCRYPTED PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/auth3/keys/authority_signing_key sbws-1.1.0/tests/integration/net/auth3/keys/authority_signing_key --- sbws-1.0.2/tests/integration/net/auth3/keys/authority_signing_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth3/keys/authority_signing_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAsFTr1o5iNvK0yqQRfNcUBk81BLNKI1Uv9uCy6mDdneYwKZqV +eB5w5NQe8qFSGWzLWWfi7ZdQaOPHRUJWqdtkuR6AFEh9Ws+wSU17Jd1uMKfVFQL0 ++4q9tt52yoCE2tJ2GFwcouirUGXCXFQbfCrmUWxajSgGpDGNvjf24td8kq/O53cl +4gOpd1Ubl2BsLxALUswRsurJqLt1r+WNKEV9gBXWavyWxZcOwBWUhExdWWVSlM2O +2hYsDq/VV8MJHugHHAju0d7JYhCy8L4K/AsZJ9KDgAyXw7L7ur7vqIzX/EjqtOjP +k+rKdpe9d3SbxdHRN0hmwqx7n4zGDt0AD4sQ1QIDAQABAoIBAEZq43PfkKFY85Ip +S0KLsdeDMi0aXHUXw/vXaBvpaxdvobQ7o41AutznSSSj/+FfbSXbMuinENmSfTXP +dESI9QRohcWT3VuyrTfY1WEduK99lr65W5RIp9KrrMzZ3jR07+xyfI0iVafZu3Cb +tg40I55IozDT1zMjBvP77YBRhdId8dGkftSQ8+bDtcV14lLSpwSDQefSa0LNqSWm +Zr/mCV5S2bL4wJq7hBFS3VYfrGPt1B+r+pGFbBBstj5Q78YmzEUPZ0fN2hgym09l +8HdHed1oDm9K/66fPs+VgXkkrM3CCR/Hel9D91l9hVvXia9qAA7c0G1twbIg7g/a +yKeIQgECgYEA1u58v3a2fUIiq/hjJdMIK1ebCxI6ndhkWZn5wfGShv0sjDb7mNyN +wb8+m59an+YhfjinazHl4TVcAdD2lDMWbZGEcaSsXbPhDcEYFhVbag/IcuFlBOA7 +ZJwwPZMirT920X5xAdJcAeAhRnBjQAmJc8L4oIDlKCGOJnBW0FKpR2ECgYEA0gZM +IRPvikTZ454Xw947WLFCuek5ZOzupPRRAh+vd+WVmX5HnjazKFWOuFO9F119RAKg +0NonAQomGexgkQW75oC9QdSa6DKeumlswY5hQ8NkNlfmsZYgP18mplvv/0i+8BLl +kdPihrmM0Sj7KLnIHau+i+YB3euZcT9cq0Y+YfUCgYAiS1FaV33MxLW1jN99FIwU +JllVzeXOc82nQltIp9wCb/kgslE41dBEnthFioqyQs0LT8ze3MpaQeJZY9dEVFZ6 +yyI/48+g7+e0AyBtvaClbrlEI1S0D3dKmWVVHdoFnL+/s5YifUczAWktzRb5C8yD +zRNQwOBPo/MDjR89BEOAYQKBgEieLMxzHCvuynfNPeJXgKWWBMBLR7EByzH119yo +H/+Qvj0oUJL/zspvar2JFqRxitJtJjWQeFP/slTcSjdaiDGW6dlNLZKDjiZeNWBn +XnITqR6xHe7hPZ0rxS1YNI9ME9jmb8IQRQ7YMTxQsybNtcesjmHI0XRt9cwWYHBI +BWHRAoGBAIbUvxX966e7uW5JjBoo+cDPODUHQ2bQ0EHFjhmFJXBpsSVS4tsfV47Q +ZLrm4va08QfetX28yRFchn12LUtkKWrHwJYpaYvd0vJK7VL9IajemExWAoCVtkAO +Zxy1hi+uDPnxbkYFBBLh8Ew4BBnLVP1Kxj+2Y8Fqw1GsDEprA4QT +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth3/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth3/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth3/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth3/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth3/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth3/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth3/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth3/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/auth3/keys/secret_id_key sbws-1.1.0/tests/integration/net/auth3/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/auth3/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth3/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCd8vrObZhXfmw1GbShdxi2OCkYH/NC9CapI1rNjVzrVkqE6EC9 +HNsQmdgRD7NFleFukJof4xnJcm33D1TuPDV1CIHKnGhtIVIxv+UlPQ0aViGwqANK +0dqKvWV2VOrXhKM0w1SrEqkDOrmrzeHC9RQjeB/cB+rw8A9RgeFkLjOZvwIDAQAB +AoGAdOhv83LzG2+bsAICzLBWPil7YTvk1l3Yb8CXaSWE7TvFlHJD2LYO/06fuqEg +VBUH8spdCdUSNSTZd9CFUICIH5oqBhNlnh2QBs0b6BtP+GnSC+k0VPPtijMJBcAo +DqawDrzhedMMNpfpADBxsYLwXM3LIAvcmvmgglVuobyhVuECQQDKYWAbEaumGasZ +ssK+zJfYCzKYDJP7KLQ2V5pxcWPhxTMgUW0sgQ9YSBmT8of6bizms9015oehuLmb +X/IdvrBFAkEAx8wEbmeoJ1xjym2iRTZrrzKaCXXpRNVFNC5CLld9/JdYDkrKRvn8 +2wlEPRdOzEfw4Mun5SOuaHmda8R5VHpMMwJAdJtwpYgj77uAc3klbowxvWCOUT+G +EEPeuiT1rwGXfGGEWM2x6t+d+M7qNe2bXbcXlgHypnjik1L9VsIMngjeRQJANltV +IUxmPT8vjxVAvp/NsD6OFeDUc/up9kZXjxASoLP0Kv9CBQliN51PyWcb8wvnhuEh +bn/EpXvgzj8H22nibQJAQ3ZAHYTwp7ikpRmsi0pwD8ATZPL9hb00qUlZtC0w1UFV +DWSKVtbALl2AwY1Z6L4SDsFJSEh3+ys3lNVY5CRZIA== +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/auth3/keys/secret_onion_key sbws-1.1.0/tests/integration/net/auth3/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/auth3/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth3/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQC3Atj3fFj1uwoUa8LtmxSLXHsoTuOPZ5z/Jxtk1XMotvY6IJHG +xPRxUPOdFvehBk6+MhvhPRZgelN8RmUdE50Hf+++o87RwSo0DkjeQAn10yxh9nO4 +MvYyijDsseQWDS7AGaxirWOasMpyNYu3LxstvotSKnznqWnI4jYE4JH67wIDAQAB +AoGAEYXZRmEGl4iwH2JuCJgXsHxLYoGjMkDmfFu/OLljTFuwWcJyksia6xqoKLth +PG7BLV7Y4okZ0mUEB1sqJU2SdFLNJMfcX8XDDcYxJWdw9MmIfkqs9oimR4r6b5lk +a367jDBG926sdzPpZk5zJGlMt1cuy/qPu6xL0Fxq1W7xfyECQQDqBKQ7dqT9f1cu +1MP5EggkhLR+5McMSc5gPyVX3NnqJDO7GGjpNJDVCxkLMBymi7VUYxMkN3ywsBpu +Y4ez+ce/AkEAyDOoLSRWSjjvq5Av9QilVFw17OufshVvacrVwoyFz0iafgaq1tUB +RpHZ2EGfKo8zEHB9oguO9AOJpO8DFBQY0QI/V8Fl1Oao+GbISsd+tegNJaSeCve/ +rNwAiBcl6czn6uWogICm1szfgwSmX4urMskxNnPwuaQVHizrvHmWaedBAkEAi4ZE +zMEoSi4ICn9AjPrrjjF9e5JzB3+c2BP+icm5b87frLkk0vg8HVyEgAyrytNl+S0M +waRginr0sLfKY3HmIQJAd7bHercNGGAziFZI6xx9oZbFpyx1DleaH0VSJtTwN0Uh +OTfmDEvp7mpzYwAIqbrSpUvqqA/Ygtov+7tEgPk7RA== +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/auth3/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/auth3/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/auth3/torrc sbws-1.1.0/tests/integration/net/auth3/torrc --- sbws-1.0.2/tests/integration/net/auth3/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/auth3/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,34 @@ + + DataDirectory auth3 + PidFile auth3/tor.pid + Log notice file auth3/notice.log + ShutdownWaitLength 2 + ExitRelay 0 + AuthoritativeDirectory 1 + V3AuthoritativeDirectory 1 + Address 127.10.0.3 + SocksPort 127.10.0.3:2000 + ControlPort 127.10.0.3:2001 + CookieAuthentication 1 + ORPort 127.10.0.3:2002 + DirPort 127.10.0.3:2003 + Nickname auth3 + ContactInfo pastly@torproject.org + + + TestingV3AuthInitialVotingInterval 5 + V3AuthVotingInterval 10 + TestingV3AuthInitialVoteDelay 2 + V3AuthVoteDelay 2 + TestingV3AuthInitialDistDelay 2 + V3AuthDistDelay 2 + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/exit1/fingerprint sbws-1.1.0/tests/integration/net/exit1/fingerprint --- sbws-1.0.2/tests/integration/net/exit1/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/exit1/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +exit1 270A861ABED22EC2B625198BCCD7B2B9DBFFC93C Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit1/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit1/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit1/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit1/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit1/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit1/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit1/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit1/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/exit1/keys/secret_id_key sbws-1.1.0/tests/integration/net/exit1/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/exit1/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/exit1/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQDUhrtgD+6NyBn7JEvPsWHNjGNLzeoA/Wwah6j+BMnWZDyU8LDE +yf/jmvmZa074MHNXR3ZSVblTq3JeWZ87JdV+2A+uiwtVPi6qOQAADEvxDBiCTnze +mWqCwuR1BudeKdqdkArZMw5352xo6m1p3XJQSyWde2qYRFBEX/EEGsEEGQIDAQAB +AoGAEGb9myJfpO2mo61fdH4aMET/fOA9iSoF595EeqZf9X3y9wPEgMueRjC1xtNz +WtBSdo/7X8th7EU1JHcCRQxcd7OstR7fiy81n0UMC6GrHASpVYYJXC4M4MGt2yzM +Wzadp2Qa4a1Wwpud0bXH0CB699MmEcdpNgw4RGUxKeHuEWkCQQDvpU/93Up2APZX +7whjUT4IfHUOUVaRUCJnMUJxajtM3AvtYgDEmHXSAbs0Wtl0v4qh5xfvKJ2YgfI6 +M/eBhR0fAkEA4weiSvHPUwT8Uq0f3Hc5DHLx2a9Q/qKvytCcbtgjOA3Anclzf4vG +PXsCjwYETDLOqg8zyKOwgF04dqPkDYt/xwJADKX6AdkjK/VDC0MXez53erUu7HGI +kyTam/2ylw/XT2gxzPamOboeRdZHjizZW1scswzia4IGR11cbov9nT1sWwJAfAtP +wWMdxF6U/xdYsTzzDro/KpF/z6FUK5yP3IB7vcTF2XCYirf0OONdIohHENsxz/k+ +cgH+DiThFPG3SS9PQQJAJZ2IBMIyO31Q+e2GW0NKxFyCsqHAcqBUMU9XDH2POm+K +3CDVyHvc/GsZhWYayQzD65VUERjAbxyALDotSIODGQ== +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/exit1/keys/secret_onion_key sbws-1.1.0/tests/integration/net/exit1/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/exit1/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/exit1/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDHYG2e/zXbe3IVjx/kJ5cqVmc4p11otnJUMdmGA2hP+Oi7QbEJ +2EMHO9mLKsWyX42ekPCjXLeYF4NG7QGEhKbs9yzneIIS37uucMGgfwv5QLX5KDWJ +5tPLOtZmIjm4kJsodj6+OFv95Um3+QNbCoBTMAL4UfhXSyvSP8NY03obBQIDAQAB +AoGBAKYzlC4+wT69oVSngwZGmpUIgfOQUSXBMX1OMO6uWH6SGDBBiruSNA4LrkV8 +2mDflsbgPDlySPnYX+74E5PMoPjgAcZq/+dvwTfCeZZhbACqYttdLwdTZxZWKAOi +K8ffrD81pjIJKB8tpP7swUC6zrI70VuxJKfsU7KfBJDSElcBAkEA6tQJDjaCpFvx +TRQjHI7vanUVJ65TigPGwZqb+iQgXYDymc3p8BnHtd0bTuYnl35x421dFTCfF6V7 +/4ceOlmDvQJBANlaJpfZl0f3c6WtTF/3Mwr7P/7UfWOuHvPTCsY1QvmdfuosV3P1 +Zhq2NyQYttw8j7cbLPA6zNojacTeZjO/ROkCQBUmuhYUpNnHCkoGCU+WNExjdV9Y +nV+uDWX81hXG0T960N83o92jhNCFtOL7ag+ELR/uBtdUxM+tcSjH89oPnAUCQQCX +j1BoBJeD3F4tAhnGtTZHiwmF5aAF2Q3GUbwqmEs6igNMwVy+BDmNmM/8gKtKCYTh +VrbQUQA1gAop+DbmEJK5AkA+13WiKrj/JM1/CiEWwcX0VxOL/1FsaKgKJVnFEi5+ +kVmt9mFD09UXukpxJOIVLUNGCBBIdnm+7s3WZNRDg/b9 +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit1/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit1/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/exit1/torrc sbws-1.1.0/tests/integration/net/exit1/torrc --- sbws-1.0.2/tests/integration/net/exit1/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/exit1/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,27 @@ + + DataDirectory exit1 + PidFile exit1/tor.pid + Log notice file exit1/notice.log + ShutdownWaitLength 2 + ExitRelay 1 + IPv6Exit 1 + ExitPolicy accept *:* + ExitPolicy reject *:* + Address 127.10.0.11 + SocksPort 127.10.0.11:2000 + ControlPort 127.10.0.11:2001 + CookieAuthentication 1 + ORPort 127.10.0.11:2002 + DirPort 127.10.0.11:2003 + Nickname exit1 + ContactInfo pastly@torproject.org + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/exit2/fingerprint sbws-1.1.0/tests/integration/net/exit2/fingerprint --- sbws-1.0.2/tests/integration/net/exit2/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/exit2/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +exit2 C0606B414423F9A2BBA2679B440056E3B07FEC85 Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit2/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit2/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit2/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit2/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit2/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit2/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit2/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit2/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/exit2/keys/secret_id_key sbws-1.1.0/tests/integration/net/exit2/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/exit2/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/exit2/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDPSSR++AXcN9GK8J65iaQmPPEJVp895tsGD2nC9IC2ye5MwRBr +W68grl7Jq0lK2hBNP49V+JpGn9/zvouxZ/BC0iXPPOYn9UfN80a0BIlb6L5h9C5+ +YSJ18J4sTk4Nl/j2Cm5NYDHt67YQ02aBWta7hE2hsL4okytCpu4b8WjRIwIDAQAB +AoGBAJILx8IJwB9D2Mia9kplVxl18KISvQnhQMpJS8wHdzTSm6rKkGYD6CUPlkti ++rXZZIj40mXRTematTDIPSZWxCkbcKG6E3pAlJgmwONry7R28uB+PX8G258+7SCB +yeWMtha0OajMslHj+8nV5pW9b9qyJ00vCPZIFdSMXi598MLhAkEA8UCJMa1zAfyW +JB5q+3gUaIE4sJgn+Nyh10RLyEtZo3OaRjjHgV3doDI+YG4GEAyEkLWra/kaXVUi +V3H+l48C8wJBANv1DR+4+VH4wZQsVksNkDP1//iA0yjFtW/dducehYDLe9qmUWpc +vV6r8ibyfvZVO5016YmWkmHKJbtGdC6xpRECQECFCOvvPj2+xoycUfI02Ahm3rlj +x/TnVKUUj8veUvfCt/SlxL1VVCs+pK33Xsm1/1IQM+spcKKbXRbT1snDpn8CQQCU +mpG8Tec7x1lZdz7xw/8Z6+h9BuQilriHFFPBv/AoXNGXnmhA+NkD/h1CzPCUEm+N +PiBC0ZSiNsWZKfdSmLhRAkBIwXvHkNJWMLMqr+U7C1AcFyFTojpmzQ9zhmKsnIYk +FnGysw3OT0vx/qqJzML+ZbTAG7BkFwOJSWM+EbHO3f+6 +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/exit2/keys/secret_onion_key sbws-1.1.0/tests/integration/net/exit2/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/exit2/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/exit2/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDRGbi8jXaEdXAKgS63kx7qlPJpW1GVL2HHVJ/xIBrsDPJ+STKV +E31isk48cg/FhcW91myJVa+uPg5CyRnA2upEz5kCf8W7KPyifV7faVelBYO/rKgz +dUqXRjcMC1sF3RpNLgRFbB9JEMoLEGqF+T1JVHyvsHaXhCGkosvRtm9azQIDAQAB +AoGBAKzVT+c6Ho5Z66QjJxRBnLpVJxGCk6p2IaH7BsDtwpL+GKndotup4mfj7dr0 +hKjFa68AP3I8yxuA25TEWG8aA6whJQ9WJJ3dqteqMI6Vv6BNLoso35aXpo+raYh+ +FeeB93gZ+Ree9gnhfLyI1ukUFcO0pq8Gc7VWI/+Kh0EcqOlBAkEA+nkTtEKzPik0 +bKhu26JgBe0eRoZ7MEfBoTQx6Aqi/3mITsU183An3CzeT7YmOY8cdxyXUUpdxG9d +Lv2TwyOJiQJBANW27mlFVHwARad+t9PbmJXsbn6lXbAVj8cM8m5BPxdwiVtvtdv+ +t/27/MJGtL5Quh5rmXev452uyv1TEZzcKiUCQQDJN4V91eu7L0z/UZylc1+iwygE +TjsewTars4u4NvtVM6Qua7340IwlFCKQdOtmaDLc+aoSUyTbDwAUJQCUzrLJAkEA +h/TkHpw4btpNdYyrrV9Z5qaOdIsG2uocmWinIXToiiTRfRXH/7g/nG9nEMCiYyBA +BvI0o2uFxd5YzGd4IO3+8QJAFDg7lcMO8IHcajS/n1OG2J1ORt+hRJ6Hsq76H69E +LIgcBsN/hjV6jVrMsjvO8r5cTw+aAVWB5Hb8sgpqpUSulQ== +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit2/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit2/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/exit2/torrc sbws-1.1.0/tests/integration/net/exit2/torrc --- sbws-1.0.2/tests/integration/net/exit2/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/exit2/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,27 @@ + + DataDirectory exit2 + PidFile exit2/tor.pid + Log notice file exit2/notice.log + ShutdownWaitLength 2 + ExitRelay 1 + IPv6Exit 1 + ExitPolicy accept *:* + ExitPolicy reject *:* + Address 127.10.0.12 + SocksPort 127.10.0.12:2000 + ControlPort 127.10.0.12:2001 + CookieAuthentication 1 + ORPort 127.10.0.12:2002 + DirPort 127.10.0.12:2003 + Nickname exit2 + ContactInfo pastly@torproject.org + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/exit3/fingerprint sbws-1.1.0/tests/integration/net/exit3/fingerprint --- sbws-1.0.2/tests/integration/net/exit3/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/exit3/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +exit3 FC264325EA99D597FF94DA88379DABB64304DD9D Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit3/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit3/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit3/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit3/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit3/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit3/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit3/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit3/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/exit3/keys/secret_id_key sbws-1.1.0/tests/integration/net/exit3/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/exit3/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/exit3/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCskSKJ8IEBm3uhXifX8y6rak0vK53tT/CAU9FruMjBgd/aTS+m +927TYngFTyWXYL3mo7xD5Cs8Gpsoe932iq/KAPp+/HCb71OHFwN5/hgtG/Yfpj/c +xtlId4V+3KEnnr6opO6B3j7JNwTJJeRmpgeQyfS6Unh0vL+PycSgcPCsywIDAQAB +AoGBAJwQAYFoLPNMLwmnwjbNaaaCU11Wf9OvmNwlcV6PVZeTIeQmK/M/EE5BXjPG ++UMmrXcZGtj3T5ZipXF+XCVsFT8IGjX2xVHCa6Uf4lL4jEJb4pVIMEQEEd989g3T +RTS2ou9HRl9URrMUdakeysKqFu+tPh4f+Jjyi0bZ4HfkyxoxAkEA5GRnvu28R1ac +J3KF+kE1MdJhHnqET4MQOyhW/485aa2/Ym6uFaQhiXatNwQUHsF+j+p+jYXEHy8S +fBtqgPeyuQJBAMFtN5DgagsF8VN5SdXk+e1BC6yCJkUugpEyXHH4ArCmrtPXMxg2 +fBvMJzjGh6kgLvMAYkecrz7jkKLGiQxMaaMCQGx5v8iwM4eSfFJEE5w70l8ac+Q8 +/pChSFlAnKl+xh8KVecTeH1w2jjm8/g4aLH+5vTFxFvFV9QT0Gy/vMMKYokCQGAz +hMZehv9ShlMg6NMkHhWdG0RCOux/lFrItGfOlO/tBti/mF77gkCFCiDXxNMd+ZvM +XhUF3bcmkWIT4kemnYcCQD2TT1a9CA6yRK76+P6ODnqCG+d+swJrDMAXG6mfEmPn +cBmcurapsowrmj8oKHdW8o45k9gKiWPIvG9Fmbwg1Fg= +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/exit3/keys/secret_onion_key sbws-1.1.0/tests/integration/net/exit3/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/exit3/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/exit3/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQC7cz4JfpL3t7AQWDadHv25xsNBt9QCVMLNpTPdi4FMxd4ZIGac +qZnjHVNEb89vNReK59Mufai4XnWMg+/pBMAhwNtQcn3dvVqbil+q/ixR2fhWmR4i +Ams7VfubqHhMsgxGa+o0o608MhmrV+eHLBJlMC8OiGuRzHAXRmYt0Jy7CQIDAQAB +AoGBAJbOFyniR/C9OgtcPMvZY/0y+GGFuC4L7REvCHQDSQnv4kfKtmLxW5nJPPVB +EtyAFSmuakrr03JIpD9g4gFQzhS3JcbvCXa81Qg2PUFo30Cuqr5W+6nAYJB9Z6n1 +dYECnIXuAYzcmJSAKiYWRFcG/MqpHOxsAJ2P8xzO6BRyiZURAkEA8+Uj7WSxL2+R +dEfJIFjbnBqbUUWQF9fjkHzsCqpsDMHAOywnFc1lOtmyOZ5+3OePKpmk8a1oXQt3 +6HrSjCKIVQJBAMTA7ezcW0uyWkRKO/tVdQm1IrxP33Oi99bHfturUwh1rljA1fHe +aqc4aD8K31fB4373IRfvRDW2c5I8lJoIq+UCQB6TY2Wnce8YdYu2VCiR+zYev8n4 +ho/2qLi8cHsF0nXm9Ep2xo+dKEuwbv96Nz1KalBU4rDJpIPFpEqo2yxmR+ECQQDB +NszvO34UGfN2+Idqy4MBo3WXA8Dlp/ZGXuZ/BgGmXxp1Yfk3ajnGOSh2MFEftvGX +L0y6WmbbcG6R/xfXJAjZAkEAv/3H2vrkiE4jhDvj7p+A4ohL2vZ2gpwtkzUFmK6W +Ha9KAR+ITrWi31ENnQ38Nc/dy4Piub23r0RS9AZnjjmRmQ== +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/exit3/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/exit3/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/exit3/torrc sbws-1.1.0/tests/integration/net/exit3/torrc --- sbws-1.0.2/tests/integration/net/exit3/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/exit3/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,27 @@ + + DataDirectory exit3 + PidFile exit3/tor.pid + Log notice file exit3/notice.log + ShutdownWaitLength 2 + ExitRelay 1 + IPv6Exit 1 + ExitPolicy accept *:* + ExitPolicy reject *:* + Address 127.10.0.13 + SocksPort 127.10.0.13:2000 + ControlPort 127.10.0.13:2001 + CookieAuthentication 1 + ORPort 127.10.0.13:2002 + DirPort 127.10.0.13:2003 + Nickname exit3 + ContactInfo pastly@torproject.org + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/relay1/fingerprint sbws-1.1.0/tests/integration/net/relay1/fingerprint --- sbws-1.0.2/tests/integration/net/relay1/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay1/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +relay1 2ABFBACE61167A1019A56CB35B2E3362B97D8570 Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/relay1/keys/secret_id_key sbws-1.1.0/tests/integration/net/relay1/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/relay1/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay1/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDS3gAMLc0Qf2ICiU8e8jcZORk6Erv5rK8J0HpoGwZd7DsPYv6O +M1G8MVuwHQ8wiFz2ntk6n59YuH262xeMYgLmr0oBvpp2VrSgvRHhyecGR2Og9031 +5YlP2xkb9kFVTbV6395abIl+LGN8F3u7egeQtbUwB5TnXN9Xcm+rk4e4VwIDAQAB +AoGAI8NJCMe/zrt7+Ogmxh3YYGvBroq83MR/z09PY1aqBsXo4lwsxhtIEn3gigbz +q4yFD8KzHvExik+H62f7H3EEAqLN84wvCjy4vxl1/0ilgo5MYUkAemnhNtVuUHyu +YniSVHCR2To2xYhhg8dg5EGfCik5p7Lin868kEj0mCjj6DECQQD7EDvsnM/WVzzA +YiI+eZVv9X2JS4p9Kr2QOQKPvT7kbaA/2ScxOPeWw/4bTxrifRjOzmabDCqrzwgy +RxLznlx5AkEA1wNuqhAcwL1MuFNTsbv/gXn8hR2uB+Wu88+t6RZEi5RvWDkeaM16 +1ax6fJXA5Nov2hmY1k/4bZ4d4Zebg/jnTwJBALRfWW4IyxkG29KrrkaMO1yQVnyJ +FRoP1tkC3GBAairax8KXVJz8fISIkssz7fDCR0xs5TKHbzC4MP9OKMN2BqECQQCm +Lpvwy0U93tGWQakVXQWNMBhxVlA1AFvpS2HSHojaDN7GJFeRjwdacNW2xxPwjYag +nloXgnWzW5siykfDh7RXAkEA3D0LTZ6YHAQIuWayxvjoj+f3am5GduHPUu3RJfpY +faEgP/WrvaRT0pw6rJmcumsG4h0Cp0W48FQFMQ8JSB+c0g== +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/relay1/keys/secret_onion_key sbws-1.1.0/tests/integration/net/relay1/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/relay1/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay1/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC+l6cIfMBVPArIvlMSwV/9i4ZcsjCbegOysvQ4b9eXenwwluE0 +zgkqp4RyPjAZwcGIPzLduWzygFZVwchupF9Yzmg+67DyVs2rNHba7/dALC52+p9r +n+Xi/ge1gxDQr8biucE+fccRfwWX24nQGAOTqqF5tuj09ohH/KGNajvvKwIDAQAB +AoGBAKKygLMdE25vT+FnnzDOhGvDFFLDBf1jHg8I9rPKvaagt60ez2atT2PrFoQT +lCoYnWX6VSizZk5owcp5HulcRcrmp9YXRPpEIBepJWPOFLd1/TwjmqlhHnTOtDTi +cbep2HpT8k09Sm4xII7BoML1gktBc5oqDo59P6RsWxzB14uhAkEA6vvBHaAH6HBK +BZU08jsywMYqCKXZSRGiAMdYumPLqvalNB9WXwubkG3ICo7iBMVTKjPiCk+aeXYP +jOO/HbExkQJBAM+jgkebWm99IgecUsohJ6scOH8D0Ahjr76BnMZy/xTNEq9SH+lc +NxfHCADUCC34UIvIF55a/9vVVtyfgOpv9vsCQA5RInYEnxa5aLkoWx57Ht+B82Ot +fyylh2JWwwAwPmN/PZeMZT3LPkIxfiOuivTRdPy4RohsSzQlTlM+ORG9DxECQAgE +BemBw9H+AKxOipunAAMJCd1NZmCMiKJQGp9GnTZOXGRMRVMa7j2Kv3JYvSAZL/LV +fKPuW2Y/LCVEOxPR0rUCQEXgCY7yWfkwmC10zoe0IWHiV9jOSEGdFEWUElo1zHUH +qWpoxcdU01k4+sYNDTNE87pnxvbFxBluLGyNQozf0vs= +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/relay1/torrc sbws-1.1.0/tests/integration/net/relay1/torrc --- sbws-1.0.2/tests/integration/net/relay1/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay1/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,24 @@ + + DataDirectory relay1 + PidFile relay1/tor.pid + Log notice file relay1/notice.log + ShutdownWaitLength 2 + ExitRelay 0 + Address 127.10.0.4 + SocksPort 127.10.0.4:2000 + ControlPort 127.10.0.4:2001 + CookieAuthentication 1 + ORPort 127.10.0.4:2002 + DirPort 127.10.0.4:2003 + Nickname relay1 + ContactInfo pastly@torproject.org + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/relay1mbyteMAB/fingerprint sbws-1.1.0/tests/integration/net/relay1mbyteMAB/fingerprint --- sbws-1.0.2/tests/integration/net/relay1mbyteMAB/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay1mbyteMAB/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +relay1mbyteMAB 117A456C911114076BEB4E757AC48B16CC0CCC5F Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1mbyteMAB/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1mbyteMAB/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1mbyteMAB/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1mbyteMAB/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1mbyteMAB/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1mbyteMAB/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1mbyteMAB/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1mbyteMAB/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/relay1mbyteMAB/keys/secret_id_key sbws-1.1.0/tests/integration/net/relay1mbyteMAB/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/relay1mbyteMAB/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay1mbyteMAB/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQC7CSfsxnVxSgYTlL9ItQvjEnwehGUVu8zr8TZ3T6DV+UJedFCP +LWhfqJvo3ourzskXrPYUWSt7U4k/fC0113+t+QHe4lmRlvxfqMC0WxHuXbXI2fgF +X5YByYH9GmEnSDuqpxjpmYc/LJp2NVHB/w8KwaOkYCJdFLjGzPKCcgLj8QIDAQAB +AoGBAKS3J4YKMRuYSUmtUdhCOTm86bomdq9xOMMBJypj4g0O+zgbz2aIYsb7wnKO +qwqfjdFf7Ud2UCjIJLLYNSiZpEKbFSUl1eA0VqlGSa1RmiB8CytS2tMW9yFph58k +YcMoRu/a5uYJL0viKYI7kHjctKgN7708TfDsVQW8Q6htRCaRAkEA8dMa6X2/8sOS +Gke23dMiRfL/v6TSkeaUgWNL+6pgLinDbNkooMMxE0Li/vpkwHrse8+JVVHh36RC +LSOl+twxOwJBAMX/30ykFQQWUrBLKgrvjU/TyQXb3+MN+PrLo2xQTT2LdBLQYf+J +kpKIJqli7h7rh6qxZ+aNpDwP171y5q5F7MMCQQCOHNOp9/5/ujL4QsDSp6tylBV8 +GG6u02TejO4wXbXcKyrvosbfgxYqDZq84YWoIBUtxY8dlts4xJ0nTdXTEOfVAkBJ +nT46Um713A7WOLvi4PmbClSc9wigdFtzcy7JYHm3PhfjvtIQned+z1wAciWyHUON +dPP6RgMfyYwehY+6j4jdAkAAgGtpdYmYybJKRj9JO2JKHHV8fPKZwDVtU4nR5yw7 +il8Ir9dtwMWy+Mevk8aH8OZzeHrvzCCnvdAvCxgRfRfO +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/relay1mbyteMAB/keys/secret_onion_key sbws-1.1.0/tests/integration/net/relay1mbyteMAB/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/relay1mbyteMAB/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay1mbyteMAB/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDBs+qzCuXuOvN4dddarTnh1+NIpEpeSWqpSmU0V7e/dCgJ03TS +GWuUY+rEFOnzOrfvYy0ow7+eS2vCmvDLmLSp8zhJxaxGOitGn1iYtUgh9VLHWu1v +a/dWvTqkmV3OxgSmKdAGuWEfXbuL57/uGRxWOQxtHcSkySbFCTbqJXYzEwIDAQAB +AoGAGgn/oPX0FuRh2m2s94mV4GBPM3KydJ5V8lAH7tNj5h7NSxTb/WphZd9Qre9J +y+K22z1v0q0N00+8OrrtZ9EtZ/cFpCtRtHzaCnXKmsZ+HhOCREF1VbjNt6VOrRfZ +EEcJcIiXw0fXqU5/cQDN1tPZtOgBiDfFPVCsT6LlByJOPBECQQD4/xdtVK3FgRPd +GTAwraxwzY0aayCAjCUS4aCTCfv+fzjvH9pGZ7kwROGAgxPHvXs+qzP6O71d6zdM +WueOhQDNAkEAxyauZPT6ifpcJF/SBjvUfVy6Sf5oxvA1PSOJnh8M80pkvk/SQ9Cg +Z6x+zy6u6hVRIsHr3XaCAmcSya8HFXqDXwJBAIMJ7JBTMihw571CFkRoAgpTguDW +ZAKCvrgTGeiIJqAm4BWoU70dtS4b8fkqAmzp4UR34SDd86jLswxyAs7UXDkCQQCu +b9c5XstEqCKlSbjw9N7+n4Yc4dZsymIXl1UnRxNU1si/WbNW6xpVNOYuGznil6Fl +zG4IoZDnAdmXBtQxoAoJAkEAoNdrVYjgScUvlcsGtDVQG2q3OY5wt//XbPSUvC5x +LJWz91UWR2JWmKX29hLeHRwltdY8MknyRy7qIVfaaGU2vw== +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1mbyteMAB/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1mbyteMAB/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/relay1mbyteMAB/torrc sbws-1.1.0/tests/integration/net/relay1mbyteMAB/torrc --- sbws-1.0.2/tests/integration/net/relay1mbyteMAB/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay1mbyteMAB/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,25 @@ + + DataDirectory relay1mbyteMAB + PidFile relay1mbyteMAB/tor.pid + Log notice file relay1mbyteMAB/notice.log + ShutdownWaitLength 2 + ExitRelay 0 + Address 127.10.0.14 + SocksPort 127.10.0.14:2000 + ControlPort 127.10.0.14:2001 + CookieAuthentication 1 + ORPort 127.10.0.14:2002 + DirPort 127.10.0.14:2003 + Nickname relay1mbyteMAB + ContactInfo pastly@torproject.org + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + +MaxAdvertisedBandwidth 1 MByte diff -Nru sbws-1.0.2/tests/integration/net/relay1mbyteRBR/fingerprint sbws-1.1.0/tests/integration/net/relay1mbyteRBR/fingerprint --- sbws-1.0.2/tests/integration/net/relay1mbyteRBR/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay1mbyteRBR/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +relay1mbyteRBR 934E06F38A391CB71DF83ECDE05DFF5CDE3AC49D Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1mbyteRBR/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1mbyteRBR/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1mbyteRBR/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1mbyteRBR/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1mbyteRBR/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1mbyteRBR/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1mbyteRBR/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1mbyteRBR/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/relay1mbyteRBR/keys/secret_id_key sbws-1.1.0/tests/integration/net/relay1mbyteRBR/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/relay1mbyteRBR/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay1mbyteRBR/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCk3FyIv7JJ33Il3PJmnEu1h5k+7LnF79Bw1isjJln1zRtKMDFt +09NzAiM0gGaMwOhqIgi9/MlSUFIEfWi/6SFfidbDtWb5ltEYZZQJ55FTElItLPyP +z7DJpU6TTGB/jbeGt3nMQcxAUWJkBfA31N/azi39D+2tfKaeiIWxc9IUewIDAQAB +AoGATMd8KQvuQs1X4h5mnjdIY0DFtDKXyoGHuCVmbbZcy0c84G5VduriXXuvXbI3 +EHJ/S36SR7lFIZrat9CYSliO+6pZsDV89VukFdO7wh/+Ox0Hz4Pu+ZvbOzvXX1PY +VCNVy8uQaXzXmi0qJ6ga/rJf8ZbwlTY2BpFQWKtDQZdxB7ECQQDRFzzb6akAxC1N +b8JPrSS1Z5yomdNk0TN0dVNfUqptbHne/SEIndMgQN2YaucNDHxtXsEOPtwOhKCT +RTvtP8flAkEAydjaHBenCdFjmxsfL1kO+8hSljaSY8D69vh1MUesEHVUdmt7jkEB +I5uSUacCZbRegURr+c4dAZNSlw/fRJ/k3wJANV2/+8UUjIyFn4kRyDJB90b9tg6k +5Mt8XZIAeIjp1MnmBSMIRmG53DaSGG/Yjv0L2QJhNdiXY+E39r6p//DSfQJAQfo+ +IRyiHxVwh4yv/twPh8GNLUidRToZTo0cUYVp+t8/Qo4TjwY0hx0dEM+UewYgNgTV +fowe8xrizjoD8vyI5QJAGvWUZ7KdNjoogB7ZRV5S7BaKwOK4DxrQRkonqqhCzmgd +5Fi6B/I6oTCb4t7dEyGAf1808MveFlPDILy9oo8XIA== +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/relay1mbyteRBR/keys/secret_onion_key sbws-1.1.0/tests/integration/net/relay1mbyteRBR/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/relay1mbyteRBR/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay1mbyteRBR/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCmT+ACA5QDRPqy/1O/h/dEDbQPGrZO7NADg66Nv3SoQg9fJcM/ +WTdqCgZEKrop/YJS2qv+3DTMca7ceAyADZUnyHh6G4rBSIeHjpn/3OWvrNYuch7U +wWm+ZP2EH9465uEBMIU5xFKbV3DTwXYb+ckF7aYp/kR7YUnOFduE2s95PwIDAQAB +AoGAJi/g95VqvvVvuvn1eNz3YaDj3zP6+FRXzvbFdbaoSpogfXBmTH1nw+lZVv2Y +kVusOOoM5BAb73WK+QR3mk+EF6d4XAuAR0BBfs6oDySHoIkGGUvt/78/y4PjQJiq +QlbXyuM/IeIcoueGXzjrFbJS6giUoRuSfMuZu/+44JHuVMECQQDbdwc/zfkkV9To +wqTEemCEzU6SbxWjVOcmzFBVhu7J/LlMtrFJ4U6Q+POOGkEaMcRawtoc4+wSPpUf +j+WOmQDrAkEAwf+enb1HhRNE80zYf9/b3tZXvc9lEUvG/BHS0nVS5iTl2QVLE7bC +s4dpiQ+p7Bn41o0iJejJl1Y4HufbipBz/QJAMYYxr5dGYoojdIb/8YqAH1fJJEhH +BL2TD/FJK5OwbQpHsmUDqt1aAmL7axTHniae3bBc0eopCpDuy9mH/t7OEwJBALcU +3wNscweSpNqb5iHgCOcnD40RIgeNpQ+UXorBsGeD4OrLxQuoydyV6bIw9yLba+xH +Kzftlfpdov7/vz/ojZ0CQQDTzLKgAQ7C8HaLNPuX+w8SVlu7+Hf/rdlaTmppAAFa +CbZIc6BnSeDJeoI5aPyy3w/2ZwFMYbVDnCSbgO3jO33N +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay1mbyteRBR/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay1mbyteRBR/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/relay1mbyteRBR/torrc sbws-1.1.0/tests/integration/net/relay1mbyteRBR/torrc --- sbws-1.0.2/tests/integration/net/relay1mbyteRBR/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay1mbyteRBR/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,25 @@ + + DataDirectory relay1mbyteRBR + PidFile relay1mbyteRBR/tor.pid + Log notice file relay1mbyteRBR/notice.log + ShutdownWaitLength 2 + ExitRelay 0 + Address 127.10.0.15 + SocksPort 127.10.0.15:2000 + ControlPort 127.10.0.15:2001 + CookieAuthentication 1 + ORPort 127.10.0.15:2002 + DirPort 127.10.0.15:2003 + Nickname relay1mbyteRBR + ContactInfo pastly@torproject.org + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + +RelayBandwidthRate 1 MByte diff -Nru sbws-1.0.2/tests/integration/net/relay2/fingerprint sbws-1.1.0/tests/integration/net/relay2/fingerprint --- sbws-1.0.2/tests/integration/net/relay2/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay2/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +relay2 8E687E91DCAB967F6E4EE8E46E66F6AD05C7C625 Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay2/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay2/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay2/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay2/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay2/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay2/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay2/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay2/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/relay2/keys/secret_id_key sbws-1.1.0/tests/integration/net/relay2/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/relay2/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay2/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDxUgdr63thXzLDAycLvU4Ly4CAOH9r13DlTaWH3ggHFGkBD85K +kFYlhWnuq29aq6U+Yk4A5mw9KmW0ekE2pEACUsvcHXTS5EDqEkDXc1l6QX0l69RM +YfkfVwKy6WLxr7Y0RPNqckpwP8e+g2mgsYs+bYmUjsvcIVsV9AjMCNU9cQIDAQAB +AoGAOIHOvxSK9YgHKObqE7OmPNjQJDYWkDgIdYkzA5eYJaA/3kHHOmqF4Y9V+M8n +6gFbVjpKradHHSN6YUJnVtYrLmuvlDg22zwJYJK7seZIjn/kF2QSMX6zuVj3m4iK +WvAlbVUGwmv1WGMzZjps3rLuQbF7p81MGMwGEJ8bgy00s6kCQQD7mZ9Jeoayhwz3 +P14IJ/0PPaxUbzoKKBysvJ1NUerVznAjcQXpjRDdiDDfTioYpkG79Ad6kXfnSqHD +de9upU7LAkEA9Ypi3bxAEXyH7Q81qEZUDSM6VL2xaAtUU/qsi3QGk29ddo1P1i5N +l4AWmiWFNsF6R3cJAmnd1ezgmjN4VQ9BMwJAE0G2x1DxbkiX3XSkJcupurjlr7Cz +qINZbn8hKwHSD3s1agEEitOZLtt1oiHBZxGShmgvClqH6tPzfZpRMQ7F8QJAHqHV +E7SYiZXvnzN2jje35Kuwa0H/D+vZ6WAshnlDGMGb915Jx36fT3c7Wp+zrtEUYDYJ +ebObaUTUAGjLalEs4QJBAMah3/xBAplHAbVQnXXQHpn+0qsY0ktLyOjRIft8jfrY +/qPQm5C6A5S+KIjmv2AGp0TD6Fas0Bx8MONCv5oVRSE= +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/relay2/keys/secret_onion_key sbws-1.1.0/tests/integration/net/relay2/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/relay2/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay2/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQD6gT1tnd4Sei+DNvTlGy+oH9CQMnKCNvTm2ebPjN3FgOta8bTD +nvw+OPDfyzZbTyx309fRabg1GgrtSNq748sB2JotNRXq5yZpASrEq90gayGtPinY +unyypb7LzUenXH75tpypbdTrJi4+vkvn8sP1bbMG/lEPlEOE/UjRFWXNsQIDAQAB +AoGAfVai/LDLh2U5Vqbtu6BxpOZePXfwUXTb7pHCacCqygJqQDPF5UcsIJnu85rC +1Mb1kfMd/fqun2srYnRcdTxRutpR+21v+RKTF+GGo0rgQ3Ub6FU6CWg4cgC26iDR +r63QKLpwqYEkhurWFBtKOZfrIdzcsXrQ/08D2Wp/hcGM0+0CQQD+In/pTZTu2/Jk +pRXOkD2G02goqtoTw80UaVpnyN/NUWZklJAwbQ/Hg1CHRSv7JHwCB0cQCdtWZZxa +fWEvjLkLAkEA/FfrgcPVwjvYU7m54Es8hXSVUM+Y5n27wIcgyINbvY2Q/UCNNPVP +1QOsYh5T9gZPYTyDPCpBMYPP9oiDajIhswJBAJPdlvc0QNfJ+fs9YrbRjjsIfq5K +2qBtaMHbmtKQOkfIeP9EwbCrhGbdrNlIEhMKY2z6twpJ8ekPLi4ojYeRJYkCQQCk +SuXVZdiNjoMo99oSM0njJeV1aMQHBUOxdQkeI5AeWMZWbKnDGhhw+uRIObC2lKMo +Dsru6B/sCgLjYzwUbzFLAkEA2k3qUR7Tz3puN+pOKGBW9M+wn2m+HnOxiZohbyyj +5T9GeTo0Sb6gK5FBrWA109yhKJwC7LCm0pZbyE1NUCVahQ== +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay2/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay2/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/relay2/torrc sbws-1.1.0/tests/integration/net/relay2/torrc --- sbws-1.0.2/tests/integration/net/relay2/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay2/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,24 @@ + + DataDirectory relay2 + PidFile relay2/tor.pid + Log notice file relay2/notice.log + ShutdownWaitLength 2 + ExitRelay 0 + Address 127.10.0.5 + SocksPort 127.10.0.5:2000 + ControlPort 127.10.0.5:2001 + CookieAuthentication 1 + ORPort 127.10.0.5:2002 + DirPort 127.10.0.5:2003 + Nickname relay2 + ContactInfo pastly@torproject.org + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/relay3/fingerprint sbws-1.1.0/tests/integration/net/relay3/fingerprint --- sbws-1.0.2/tests/integration/net/relay3/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay3/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +relay3 693F73187624BE760AAD2A12C5ED89DB1DE044F5 Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay3/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay3/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay3/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay3/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay3/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay3/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay3/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay3/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/relay3/keys/secret_id_key sbws-1.1.0/tests/integration/net/relay3/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/relay3/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay3/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDaG0RNhIhKg/eqOrvQvYbpSGTLkEGCJ944su9uFA6Dp+e9gn+l +KaMmB0AEJg4reTGxogE2Ot0h8Kv33R9yZB4J44Toykpg/DjziSl0FwI47fTzo5aU +CkwsSMH+qk+hGsbQaYymEmped4jHwNa1iLdhPltnp2pqDcDqolQbEQxecQIDAQAB +AoGBAJhfPjuNoI22Xlrtx3KQReVKLyjLsghVA/nGgGD94Bn4Pnz142kmipUxsXZ0 +g6e5do8rp/8T5pMkiW+TarnG2fgxo7W3tjaXylLBoKL+/jOfpgOF2vpse9II+Ov2 +rT6X0JO63QJcTD4E6+9LS0gYVCho0/AlE4p0y/FrI3gnBwpVAkEA/WmjQd+mk21l +BKJ9E/1z4QN8TfeC5kvrEbOk6bYoR6VWCKVhwASkJI0O4EzDMEQSNp3JBaGooApe +SILbf1EgUwJBANxVWNOGogUVxfNbVQQB142dCSnISOrYR37MhiOD0hzYoBKtHwUu +265lg9HKjuIV8LFrY0eaYi8A/BUVYgU4PasCQQCOf/nLEC4GlyyIF6tIM20XKjOb +UESupiBQuvlQZxttdn7Tq9Q0+ycmWp/z55aXLKAlUEL7RLWjEVGFavhtNrx7AkEA +lkbTxbnO6b72+0twycHj3d+cb1X+fcazxMUEPFbdSPVrADH5tVRHW0Q7yyvnlkY2 +mxvOY/jlPH/kbxDdgQfh7wJAH9Pa+pvJn6NxjscbVR0Y0h17/nYm9uQedRd9ukyV +8XbjKgmnKnzIlQ6ySpqRMDRIcqE/AgQ1n5nFQKZ1QS9u/Q== +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/relay3/keys/secret_onion_key sbws-1.1.0/tests/integration/net/relay3/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/relay3/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay3/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDDnoSWxs6xwfjKvNT2w5COtXqI+t6ZE5s7W5aI/M4dJ8cjWj2l +3ajC0gXT5OCesoHXs7gdsRtHT9OaYzf+8NDNK7oHpXOJCn3mpOlh0iihCFICwuNX +/LBr89Hje0NogMIJjb7+52FQzMWsk/PpKidSTNLTAyVEG6u0r1AAiJz7twIDAQAB +AoGBAJ98Ao9GwMdt5F+t+lbA9H9U9/Try0W0FXAcaKuAqK5eet2YugAbqnZDcprh +1DuuP2vTyzzx4tKOA2+wirugLCpctNz2SysegQt6NXUWbnZTlBaTBEgrTKV6pTjC +d8z9S9MdUOQj6LJXRLsL/9z/2uzHAeiaw1ruMlBWg9L3oFVhAkEA6tjZtHmKfW6c +YwTI2DkUMluzBVY/k2kft93rflzPq2xdfbNAOeJbd8h/26d7AjISZYmMsRfBrmr4 +55OIpfIj8QJBANU9JNKm+Yu2bWKswlqXX6KaXzX3BhAXOUOrFmFQBpccpR1eyL/1 +EggW5bNr6J3LNVgLYPJqUSayM5mbAkSroicCQGkdwuQq+rgGCG2xuIw/kDCyJsEe +x7oddZsA8+VNeY6LJJWc5ASEstg8oC4bGs3yx5U3KggiUAjXzK0qtiFA7qECQQDS +N1T11mZstYtgm69+X3yIcGcsbDot4rcxXpjRlnLoxfSA39BIbPE5yFjqaJcJ+Cq+ +gXkxgl4+J6923rl9uTZBAkBZUdXXduTh3emRMr3Q+FUD5z0x85qNJb9wNg75gVo9 +stZN4CYEVN5rHPkJsZC8YL+ydxKI8N1Yx49h/n6UGcwg +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay3/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay3/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/relay3/torrc sbws-1.1.0/tests/integration/net/relay3/torrc --- sbws-1.0.2/tests/integration/net/relay3/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay3/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,24 @@ + + DataDirectory relay3 + PidFile relay3/tor.pid + Log notice file relay3/notice.log + ShutdownWaitLength 2 + ExitRelay 0 + Address 127.10.0.6 + SocksPort 127.10.0.6:2000 + ControlPort 127.10.0.6:2001 + CookieAuthentication 1 + ORPort 127.10.0.6:2002 + DirPort 127.10.0.6:2003 + Nickname relay3 + ContactInfo pastly@torproject.org + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/relay4/fingerprint sbws-1.1.0/tests/integration/net/relay4/fingerprint --- sbws-1.0.2/tests/integration/net/relay4/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay4/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +relay4 4D664E247E530CA5CD5176B8C1A6DABC9531F0B0 Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay4/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay4/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay4/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay4/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay4/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay4/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay4/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay4/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/relay4/keys/secret_id_key sbws-1.1.0/tests/integration/net/relay4/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/relay4/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay4/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDEAaKU5cOPUZf0Vk3XUxQf7JQ29u6I0U4/pHP2sKTl5q2Bkx2o +pBdEZ0HA6V43oZEAC6gswC+cAkq3YYUkUlt4Es1yWsZzYECYPsXbLEJOL4qRwKC0 +IRjpnPSha+K9VX3DZ9m+V6X5dlNA+u1k9ksBFZzFEfDhqGXcrW/VYddTEwIDAQAB +AoGAGaelIhwYxq+FViPduGtVXQBYPgGAI0RSG55o10czrivNVgJthV9E9F0XjCp/ +fISXOAxOjNzy3lamdO48wQ5OjmQiaH7kUH2m/v/xn0i4WCVe8OarYDV9f5+Iy4GS +jADSQu/AWBxVqGYXbVSeBFw++yjx6I5innASZ6liVU4/YxECQQDrFIBXy4DSHVW6 +UlByGmaLoz5dA9YU4/kBrTDDncZvyJke4yk/We5AKTecAnodzhgvc/KlchsKo447 +eNBS6g/dAkEA1XL5BBcUKQrUAfSrTm5Pcgg0mhlovFqCgwiO2SblAodkXFLNxyD8 +twjtYEAP9qZZxT1ej7xKBxUZ3eFVJEY3rwJAX1/Q9k21NQn7xv0wcCqv4gVX/urY +h186nmiCuUqgwKd1ICfviylUVSmjyyLiIoeEHnR/RIp32CglQ5Gvi4Q07QJANPlQ +1fQIW9tFHKslbNSxwOJKc8hElyJ7a5vPIBOoGIWTodonGfmU3rRHfXDIxXUpqGr/ +sfqQAMv63HdVEyieHwJBAIk46ICdBT3Sc/sN9JSB/WWczUNa/uaLElFIMjtd9OJz +QokMFlWLk9gRnm4wx5UaFu3B7TwAfKjBA3DJPIFjbEg= +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/relay4/keys/secret_onion_key sbws-1.1.0/tests/integration/net/relay4/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/relay4/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay4/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDc/Y7E5UsEz+t7YRL9r2OWVuTvZgc33mfLbKsGRo9oqbBD2XZb +1fwHMI6cTRDzlUiQjpAvdtiZEvIWkK27o0U2tjxMyLgB5zQSggTeoK/A5003xmwS +jxpgR5k0xAVfxDkuPF74DaTdK4VuHX+pQwxoLPjNmGRZu9gMX7dpYJGaCQIDAQAB +AoGAFK/ryITKdAXevmsE8xiTEwTJY5vCezlnsARZMjoyvJJ9/KwDx28Nq8Kg5wLT +3mmQJHAkET1n41SBsxkSGmmbVeowUuTzvak5R/kh2ghLZmpM/hU6A09OLnh8Zcoq +Ce1i4G80dsvPLoAhhIP+tQ00Upi6rF+NB3RUcSoUZ1GGewECQQD+vfqv5n9GsGJu +NgyWxdolRwl1xDcr3DjuGpsBwu3jAvO8S4RfW5Q6yCOvnodqbk0g/rYZCrRgNv6B +T4WW33zpAkEA3hTprrhH9Y7c1YP+49cCxQ2iWdTxPWALyAl1jrDvq9mkXDxts8uW +f9LA6i1d8BhATrrpYP9OFxMeCZIErB6AIQJASlS++GKmksbXAdetlrUGsUfoH7/w +ZVDuH7QNtNbfORVcb34NkxGS0BAkMZNtagXjZZRNKENyXHzPiIXesiZmOQJBALj5 +3EZeUfmTfBcP8W9e+HyfRZnjRuhjJo0Aa/1lHMwb5M26u3klWv6u0WZ8USEZYf7x +qk/Tdvy7PKqmB+RBJ2ECQQCFVN54qO+U2UtNbDUlD2ULX7iBuekAs8HCsUV6KHi3 +99VjxHPqdf2aY9TUAHelBDUzYGkAZYY5omdmdaU21OXn +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay4/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay4/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/relay4/torrc sbws-1.1.0/tests/integration/net/relay4/torrc --- sbws-1.0.2/tests/integration/net/relay4/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay4/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,24 @@ + + DataDirectory relay4 + PidFile relay4/tor.pid + Log notice file relay4/notice.log + ShutdownWaitLength 2 + ExitRelay 0 + Address 127.10.0.7 + SocksPort 127.10.0.7:2000 + ControlPort 127.10.0.7:2001 + CookieAuthentication 1 + ORPort 127.10.0.7:2002 + DirPort 127.10.0.7:2003 + Nickname relay4 + ContactInfo pastly@torproject.org + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/relay5/fingerprint sbws-1.1.0/tests/integration/net/relay5/fingerprint --- sbws-1.0.2/tests/integration/net/relay5/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay5/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +relay5 32B7178F7201F76411A99D3552F340D3597D5629 Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay5/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay5/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay5/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay5/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay5/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay5/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay5/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay5/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/relay5/keys/secret_id_key sbws-1.1.0/tests/integration/net/relay5/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/relay5/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay5/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDGsbYyn7+9tDq8dNb77bHzrNZIlVyb9731qDhPg1UyzNOXVQ4k +fd731TAWExXhjYJaxg2GzCIaOr5Y60pYiuHiBc5b8CFx8p46llVqMSVkLdRVLeDw +XcEeY7GAUoFZqDCPlG/y8LIzur6fWHSrwgpBd3slf8F5cwmC2Htk0nsdtwIDAQAB +AoGBALFkkiVKaLamxVXh/tp3kcvbQIkXCNKa8fb0FCoxOAydwD85CeJcmoerxz2+ +FmgEniAK+KKjd8LxNWZP01WP0TAVe2ub0XP1N5NUaV0NPvNwAPR4mRNscEgvprC3 +jHdfN+0EH5j6Wqdgr/VEEIDq6nxX4jtJNSgVrFZOm/G++VkRAkEA8lNdIzOyOK2C +m6XpsfAuiU/cRK/wjyGLjQQ8qXxArWMYlmy9YLdrdRVIFPK6MWQu/Vzny91hiSIU +e68djyrgCQJBANHoDEQ3Exx38AfTZPlUZuz06f7WdTOTHVHcxgLJOQIEHW9j1Or2 +5xOkZ+KZ7cUVkv1PzZXJkKUcNbQLK8Tt/78CQHK9BDGhzbZGebwnH/MvzWkY1ivz +voyXDpOTuQFrIti2PjMjg7vivU+v5vR8RasTS6iNr/d4eDFNYLN7pBxg6bkCQEg7 +Kb1vlkv9mcOXou+Jc8Gmpb7j3YDZ5wT8i8b5p3xiHh7uo54XSH0h3f3EKmXffq1c +gMpCilvG0VQbY/xoSdECQAlywnXV372n7j4n+rV1WQ0+QZmkxrUu0JnjJX8N4WKa +KpBB8hMhcu2BczYp3sA4KSz0R241F3VD5rheZqHnTK0= +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/relay5/keys/secret_onion_key sbws-1.1.0/tests/integration/net/relay5/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/relay5/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay5/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDxY2DbqbMW/2I8GRO4U9Fzt/FO4XoTaV74ZyDfpI/VAJrgl4lr +h2lDlt9oM/MR9Wp3xtsdx/9OA0lwfO7qugNTB91jUkNmoOhN7sNCM1H/iskw+ka0 +KHI+wLSFJwec9GIiox8K8sOLzwlrpYHxruXR6551bayVW8OsNwKOg9gF/QIDAQAB +AoGATZVrZC6+6P1cTYSh2fTWtSLB5GCEJAkSBTTgVAFIWWXAYb9S48S6CIdNduh7 +j6IZfjXj50aIwEI+MHF3OOpgdoeaYTm4UlhG/mIw2WiGRTsXnLMOHMKnCWBwKXQ8 +xgXmVMy7o4mzylctIrGlKO32eMY0ONSTEQNXZTBZ124mYOECQQD7Jw0kySDTyHda +rYWLEBYnEytz/PpJHMOWpqr+l3RHcjjFEBIjM7DLPGVVjNiiiHSdhCM9SHgrDlZa +6bx2bz9vAkEA9gwUynsyDorOAg78CGtbnYClbm7ehJMTCj308HTlQ68gkbrFBVY/ +ajATpba+ZPI8MN+0YQ3/8bMwn+zHdGZbUwJBAKywD0OJ375/CkIZKceSigjYD3qz +KUz4MCv38X8YmGU/znUBNddqhVdY8bw/Gf23oadk4e4TVD0WoJ3mNSWiHJUCQGqw +vms2lHQd2EqoOL1l4Coh2JpUVQTjyYPNbeK+rZN14weOF1TDG2huRHa9ET4wk80V +k9/p9CvYaaNPRwXARKUCQDUBe+5cxrMzq1ednHAtiQIASKs1h5GZlCJB778hMod3 +OggdXyCTDBGDXLBd+SXBbKrTMivKe0BIlqIpONugTjU= +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay5/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay5/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/relay5/torrc sbws-1.1.0/tests/integration/net/relay5/torrc --- sbws-1.0.2/tests/integration/net/relay5/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay5/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,24 @@ + + DataDirectory relay5 + PidFile relay5/tor.pid + Log notice file relay5/notice.log + ShutdownWaitLength 2 + ExitRelay 0 + Address 127.10.0.8 + SocksPort 127.10.0.8:2000 + ControlPort 127.10.0.8:2001 + CookieAuthentication 1 + ORPort 127.10.0.8:2002 + DirPort 127.10.0.8:2003 + Nickname relay5 + ContactInfo pastly@torproject.org + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/relay6/fingerprint sbws-1.1.0/tests/integration/net/relay6/fingerprint --- sbws-1.0.2/tests/integration/net/relay6/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay6/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +relay6 C7C5094677013F5BC124183C71A482D0156CDCFE Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay6/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay6/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay6/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay6/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay6/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay6/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay6/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay6/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/relay6/keys/secret_id_key sbws-1.1.0/tests/integration/net/relay6/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/relay6/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay6/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDM4WCrKcd06PX34O6NScFAoUhvLw36vE4kvDPcbjaCUtg4f/YM +uxNqRYiRyF6CrnK8QQ0+BILSY3fvleayq+oMIOOp0/6iXZN2DJuyUT5zfwRgaxfn +REAnn4b3lOV5ROE9OeAj5QSlf786Dz6r1bSr5anDO1lmp7zlrJaRX/uo9QIDAQAB +AoGBAL8mc8OeKjqYc4x+rvLhYk2DbIpXc4qBD8NUWGqL5Y1378XfFVhRXk7jawTg +1dTFH8UmSE2ZddkG9spwqBoBLU3R3bc+zORHUKxmzKTSzQxv7IqWjFk8R1wLztPD +McjRxjtbDlt5reRzAayTNo9Jr/2KXu+uVDAzZGs3WXBfrqYBAkEA7lZrcH8EUyW+ +uNfuo83r2/tgv6a59/Z68VAPrzF2vS4gPYV/a5ICMC/m+xn7V65Fi/sNpkZvTvVy +19Z9FzzPdQJBANwQOwH+BQ88NXF6XzOYxJNNJhdfgnq4g4HTrmRUZ6rikurfDzQQ +KQ/NoBpn+iHgkkbBmp5QmO/ZAm8cDAXtw4ECQDN5Qpb3N63ldv1g1U9XR9sovUpm +7b8Z4o9/ZQPHQQe2kIv46v4GeKaDkfUlNV1IbAjBXb9Ncviwle77ieSc44kCQA3O +tuUZGxgpnvDPzMlf/HWBkbJFu9oXWaj7ryV8ZkTCkpVKCZoqqWjkbfc6LueRP5xQ +6XKle/MQmqZ+DAMTOAECQDKLWLO/6vng8CrJsDv5e/3kjFAFSIN5fl25TVZn5Yje +ImUI3OIWKth4bSogQvVj1M9zU+0D0U4z1/03wMZnEWo= +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/relay6/keys/secret_onion_key sbws-1.1.0/tests/integration/net/relay6/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/relay6/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay6/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDWLnWvQkwURtn3LBgL63OPC2ZQ4kTpMqtRxcHgxz37rFPmweUX +aVipkt2v2YDKlbLYO2gaEYexegta3obDO5g8+w6DTZ2DzZScPyh4LbA3O48VS31T +/MN/CYYQtBpIJvKZuV61TJZf/Eb34KkIsvngyPWvvIlj/L+2S5veTh45yQIDAQAB +AoGAT0Y49tqCEEs9J9hhOiA20G42u59UREAl2wloPktgrMNW9qUzpxut3m3mscy+ +d706ygALAwMUB2OVylds59wp91IwntCPtxc7kGe6nBXPipxUOxpnS74PaSjVo3P0 +1Z/tq+5Nx/DQzGyEJdyeGWLZDILjZCdA1CWBYHflC4nqFMUCQQD/jVKHwSq1/Tg2 +GdaI0WpYGeuDDGHOrOaJQnPER45bzeYDzolyCdTVRgnFgnaaAMuEPNmK6gOtXg0E +RibDUgrbAkEA1o6Sj6bNbPaplLP01YQbvHlhT0KKQSLQhGnw3R55SBMMFfaaPRda +bvEd4oqRCD2oLQ0bcKuX1wLz+XxUBYplKwJBAL/gJWJmqCIC4YzFc65Axn8l3w4D +YwCiE2pl6bQrIflOLHEZR1vHg/UqZDXXEiPpiuVcJt7FfENUhaN65Kns1NcCQE+c +66k+Ha+2/0ncVJb9xET1TTRZ3m84+eKIZQXHjv09hBiKPsU4aygLdClhfVtvdX3y +BSKTqW3w/JKwnYTIy60CQQCryzKc4d+/OFjBQev5Qy8KkeINTwCEcS9/PqBN546W +9US8JP4WbYFaJZLnSUHod+CBj7axtEru+mFK3sY8D9Wd +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay6/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay6/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/relay6/torrc sbws-1.1.0/tests/integration/net/relay6/torrc --- sbws-1.0.2/tests/integration/net/relay6/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay6/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,24 @@ + + DataDirectory relay6 + PidFile relay6/tor.pid + Log notice file relay6/notice.log + ShutdownWaitLength 2 + ExitRelay 0 + Address 127.10.0.9 + SocksPort 127.10.0.9:2000 + ControlPort 127.10.0.9:2001 + CookieAuthentication 1 + ORPort 127.10.0.9:2002 + DirPort 127.10.0.9:2003 + Nickname relay6 + ContactInfo pastly@torproject.org + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/relay7/fingerprint sbws-1.1.0/tests/integration/net/relay7/fingerprint --- sbws-1.0.2/tests/integration/net/relay7/fingerprint 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay7/fingerprint 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1 @@ +relay7 E894C65997F8EC96558B554176EEEA39C6A43EF6 Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay7/keys/ed25519_master_id_public_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay7/keys/ed25519_master_id_public_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay7/keys/ed25519_master_id_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay7/keys/ed25519_master_id_secret_key differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay7/keys/ed25519_signing_cert and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay7/keys/ed25519_signing_cert differ Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay7/keys/ed25519_signing_secret_key and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay7/keys/ed25519_signing_secret_key differ diff -Nru sbws-1.0.2/tests/integration/net/relay7/keys/secret_id_key sbws-1.1.0/tests/integration/net/relay7/keys/secret_id_key --- sbws-1.0.2/tests/integration/net/relay7/keys/secret_id_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay7/keys/secret_id_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQC8w+hoJy7Y5LTtIKXet4k7iFLAHix9h/0U+QrUJxpLWAJ2quEb +antK/7yFu+/XUZtK70bSpqCWOzvVvjVjzaCK0hmAbnk8Gt8CinwOJ+KQ1W6tq2D8 +j9HUf6UHRt6lqgTt3aA9LOZk8AFNihpbVy9pI5V6PX0fR1phSVXHrLsXlwIDAQAB +AoGAMIVzIgKgaiQv1ZxExsEKx4C9NKEM8p+YEGILxIjgm2leCL7X6kIuuxKNlRrA +Ber2Lgbff5pRQQyszr573tGmaFboyMdEE1NCYz46BLCSN7L1eQQZgkAZLM/j3OP0 +nLfa9XjL/oux/WAuRm5eL/m7/WYj0smioQe0DrET/oz+/+ECQQD2JDHgeCxWR/dq +agZj1kxH/L4D/KNLDkzL/EnsLdoFAzfMZ+t7ioHeszDAMHvjfbieozUj/cXgXGeF +bbR2KAh5AkEAxFNognEciwez25Nt/7IOraTQeijF7fCWynlYXHYBV4Jvh2VfGc+6 +jRnbjj4luL9ZYXWNr62RG4pLQw9wiPI8jwJBAOSqc3upqObvLLc7kHwnle46qvxh +NAKCYm4PD03OuwB1IN1Qnb/DySrvJZcmc7HAFkeqL7AkDjSFI9cdN82dG2ECQBWS +WJxkk09PdFq/E0oSJXy87E9P5ZekZN0wgh3+tV0JLvoAkHeElKnmWnKBr6FiHQ8R +XJUeDCCrjGN88c+KVk8CQQCFYcWCA4Pz/g/VfEyQHDdhhizotJHACJZe9Kr+7vNJ +9Pf/pWbjQnQMpbJl9wTev8lDxYbgKshOh8iqhq2ynCAY +-----END RSA PRIVATE KEY----- diff -Nru sbws-1.0.2/tests/integration/net/relay7/keys/secret_onion_key sbws-1.1.0/tests/integration/net/relay7/keys/secret_onion_key --- sbws-1.0.2/tests/integration/net/relay7/keys/secret_onion_key 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay7/keys/secret_onion_key 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDet1J5F17VC0u9mV5X8xkTdQeTdykQ/v3Zxt+gcQOgF9GFwgpS +7HUnp6BSGEjZZzt9tYZR0it2WvdO4gxLXLWb2jqo6zK0RX9bpH9UF6AmwHfQz8CB +JFRuWw6Ao27uZWpZZmx/075FqAXtEboXwpgASJFW43HVQgHuggHGFR1gxQIDAQAB +AoGBAMduRwaxu7RwKgF8XXPJ9pFqwT72fCqA+1VQdR1ZNdLcoTzyNYZtwLairrf0 +kP9EJox5yO5pkYupBz12w2WUffWGuushqrDn+3CcRuQw+gGkZRE8lpUnQwPPynWP +428854TfojqgEelzUa128HDJkear7Dtfsz31D+Yt4cOcGQfBAkEA8WccQMfhiSRO +DKLazpXMq0Dufrh6cov+turi04vvoC/6lgc+Hreh62CJVVhp+UDzP0QWRU/5LrKu +AlLMc2BFFQJBAOwu8a4oEbYBzqAB/L40s9bZJi10FK47OGOz6z2q7SU2UH32EeZR +OTWM3aeDSpad7HkFHvVtaU+5K9cJy6ZQ+PECQGQnOUuPIf05+L+5WTpYX/+1Ar3E +X9jiSB0vuke2SQaWoUpZWuZ3nVAATzn/Yogs8D6RSwQat/Et5I/GNliv2jkCQFXb +et/kkQuo/IQONOKwJfAkYEyFtj7gKE+WSB3S1QKCDCC+IyOmwzVEUA9lGuhF4IDd +67MsYuwoVFeHqBB9vwECQQCALP4WoHri8nbRVwjlHBSDjrBB5Cn5KF3CQ9Yfcr+u +Xt0zOoEE+1CuT6HWSn4zXvjsAdj0SNWiZaNP/dvfdWB0 +-----END RSA PRIVATE KEY----- Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net/relay7/keys/secret_onion_key_ntor and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net/relay7/keys/secret_onion_key_ntor differ diff -Nru sbws-1.0.2/tests/integration/net/relay7/torrc sbws-1.1.0/tests/integration/net/relay7/torrc --- sbws-1.0.2/tests/integration/net/relay7/torrc 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/relay7/torrc 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,24 @@ + + DataDirectory relay7 + PidFile relay7/tor.pid + Log notice file relay7/notice.log + ShutdownWaitLength 2 + ExitRelay 0 + Address 127.10.0.10 + SocksPort 127.10.0.10:2000 + ControlPort 127.10.0.10:2001 + CookieAuthentication 1 + ORPort 127.10.0.10:2002 + DirPort 127.10.0.10:2003 + Nickname relay7 + ContactInfo pastly@torproject.org + +DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 +DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 +DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + + TestingTorNetwork 1 + NumCPUs 1 + LogTimeGranularity 1 + SafeLogging 0 + diff -Nru sbws-1.0.2/tests/integration/net/start.sh sbws-1.1.0/tests/integration/net/start.sh --- sbws-1.0.2/tests/integration/net/start.sh 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/start.sh 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -e +function cleanup { + #kill -INT $(cat {auth,relay,exit}*/tor.pid) + #kill -INT $(jobs -p) + echo -n '' +} +trap cleanup EXIT + + +cd $(dirname $0) +for A in {auth,relay,exit}* +do + tor -f $A/torrc --quiet & +done diff -Nru sbws-1.0.2/tests/integration/net/stop.sh sbws-1.1.0/tests/integration/net/stop.sh --- sbws-1.0.2/tests/integration/net/stop.sh 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/stop.sh 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +cd $(dirname $0) +kill -INT $(cat {auth,relay,exit}*/tor.pid) diff -Nru sbws-1.0.2/tests/integration/net/wait.py sbws-1.1.0/tests/integration/net/wait.py --- sbws-1.0.2/tests/integration/net/wait.py 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/net/wait.py 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +from argparse import RawTextHelpFormatter, ArgumentParser +from stem.control import Controller +import time +import os +import logging + +logger = logging.getLogger(__name__) + + +def get_controller(addr, port): + cont = Controller.from_port(addr, port) + cont.authenticate() + return cont + + +def get_is_bootstrapped(cont, timeout=60): + start_time = time.time() + while start_time + timeout > time.time(): + line = cont.get_info('status/bootstrap-phase') + state, _, progress, *_ = line.split() + progress = int(progress.split('=')[1]) + if state == 'NOTICE' and progress == 100: + logger.debug('Tor is bootstrapped') + return True + time.sleep(1) + logger.debug("Tor didn't bootstrap before timeout. Last line: %s", line) + return False + + +def get_has_full_consensus(cont, network_size, timeout=60): + start_time = time.time() + while start_time + timeout > time.time(): + relays = [r for r in cont.get_network_statuses()] + if len(relays) == network_size: + logger.debug('Tor has correct network size %d', + network_size) + return True + elif len(relays) > network_size: + logger.warning('Tor has more relays than expected. %d vs %d', + len(relays), network_size) + return True + time.sleep(1) + logger.debug('Tor didn\'t reach expected network size %d before ' + 'timeout', network_size) + return False + + +def is_tor_ready(addr, port, network_size): + name = '{}:{}'.format(addr, port) + with get_controller(addr, port) as cont: + if not get_is_bootstrapped(cont): + logger.warning('%s not bootstrapped, Tor not ready', name) + return False + if not get_has_full_consensus(cont, network_size): + logger.warning('%s doesn\'t have full consensus, Tor not ready', + name) + return False + logger.info('%s is ready', name) + return True + + +def extract_control_port_info(torrc_fname): + with open(torrc_fname, 'rt') as fd: + for line in fd: + if 'ControlPort' not in line: + continue + line = line.strip() + info = line.split()[1] + addr, port = info.split(':') + return addr, int(port) + + +def main(args): + for datadir in args.datadir: + logger.info('Checking if %s is ready', datadir) + addr, port = extract_control_port_info(os.path.join(datadir, 'torrc')) + if not is_tor_ready(addr, port, network_size=args.size): + return 1 + # If we got to this point, it seems like every relay is completely ready. + # Do one more check to make sure that's still the case. + for datadir in args.datadir: + logger.info('Verifying %s is still ready', datadir) + addr, port = extract_control_port_info(os.path.join(datadir, 'torrc')) + if not is_tor_ready(addr, port, network_size=args.size): + return 1 + return 0 + + +if __name__ == '__main__': + desc = ''' +Given the data directories for a local tor network, connect to the control +socket in each directory and verify that the tor on the other end of the socket +is fully bootstrapped and has the right size of consensus. + +The "right size of consensus" is determined based on the number of data +directories given to check. If that is not okay to assume (for example, there +are some Tor client [non-relay] data directories given to check), then specify +the size manually with --size. + +Waits up to 60 seconds for each check for each tor. + +- In the worst case, this script will take a long time to run (if every tor + suddenly passes each check after 59 seconds). +- In the normal failure case, this script will take about 60 seconds to run + (the first tor is not ready and fails its checks). +- In the normal case, it will run very quickly (every tor is bootstrapped and + ready). + +Exits with 0 if everything is good. Otherwise exits with a postive integer. +''' + parser = ArgumentParser( + formatter_class=RawTextHelpFormatter, description=desc) + parser.add_argument('-s', '--size', type=int, help='If given, don\'t ' + 'assume the network size based on the number of ' + 'datadirs, but use this size instead.') + parser.add_argument('-d', '--debug', action='store_true') + parser.add_argument('datadir', nargs='+', type=str) + args = parser.parse_args() + + if args.debug: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.WARNING) + ch = logging.StreamHandler() + formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + ch.setFormatter(formatter) + logger.addHandler(ch) + + if not args.size: + args.size = len(args.datadir) + + try: + exit(main(args)) + except KeyboardInterrupt: + pass Binary files /tmp/tmpUteg4i/NmsTlmLFeT/sbws-1.0.2/tests/integration/net.tar and /tmp/tmpUteg4i/NL0vA1Yx0S/sbws-1.1.0/tests/integration/net.tar differ diff -Nru sbws-1.0.2/tests/integration/sbws_testnet.ini sbws-1.1.0/tests/integration/sbws_testnet.ini --- sbws-1.0.2/tests/integration/sbws_testnet.ini 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/sbws_testnet.ini 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,30 @@ +[general] +# Days into the past that measurements are considered valid +data_period = 1 + +[paths] +sbws_home = /tmp/.sbws + +[scanner] +country = ZZ + +[destinations] +local = on + +[destinations.local] +; url = https://localhost:28888/sbws.bin +url = http://127.0.0.1:28888/sbws.bin +verify = False +country = ZZ + +[tor] +extra_lines = + DirAuthority auth1 orport=2002 no-v2 v3ident=D7DBC517EFD2BA1A5012CF1BD0BB38F17C8160BD 127.10.0.1:2003 AA45C13025C037F056E734169891878ED0880231 + DirAuthority auth2 orport=2002 no-v2 v3ident=4EE103A081F400E6622F5461D51782B876BB5C24 127.10.0.2:2003 E7B3C9A0040D628DAC88B0251AE6334D28E8F531 + DirAuthority auth3 orport=2002 no-v2 v3ident=8B85069C7FC0593801E6491A34100264FCE28980 127.10.0.3:2003 35E3B8BB71C81355649AEC5862ECB7ED7EFDBC5C + TestingTorNetwork 1 + NumCPUs 1 + +[logging] +level = debug +to_stdout_level = ${level} \ No newline at end of file diff -Nru sbws-1.0.2/tests/integration/util/test_requests.py sbws-1.1.0/tests/integration/util/test_requests.py --- sbws-1.0.2/tests/integration/util/test_requests.py 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/integration/util/test_requests.py 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,23 @@ +"""Integration tests for requests.""" +import requests +import uuid + +from sbws import settings +from sbws.util import requests as requests_utils + + +def test_make_session(conf, persistent_launch_tor, dests): + uuid_str = str(uuid.uuid4()) + settings.init_http_headers(conf.get('scanner', 'nickname'), uuid_str, + str(persistent_launch_tor.get_version())) + session = requests_utils.make_session( + persistent_launch_tor, conf.getfloat('general', 'http_timeout')) + assert session._timeout == conf.getfloat('general', 'http_timeout') + + # Because there is not an stream attached to a circuit, this will timeout. + response = None + try: + response = session.get(dests.next().url, verify=False) + except requests.exceptions.ConnectTimeout: + pass + assert response is None diff -Nru sbws-1.0.2/tests/integration/util/test_stem.py sbws-1.1.0/tests/integration/util/test_stem.py --- sbws-1.0.2/tests/integration/util/test_stem.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/integration/util/test_stem.py 2019-03-29 13:47:26.000000000 +0000 @@ -4,3 +4,25 @@ def test_launch_and_okay(persistent_launch_tor): cont = persistent_launch_tor assert stem_utils.is_bootstrapped(cont) + + +def test_set_torrc_runtime_option_succesful(persistent_launch_tor): + controller = persistent_launch_tor + runtime_options = controller.get_conf_map(['__LeaveStreamsUnattached']) + assert runtime_options == {'__LeaveStreamsUnattached': ['1']} + + +def test_set_torrc_runtime_invalidrequest_option_fail(persistent_launch_tor): + controller = persistent_launch_tor + try: + controller.set_conf('ControlSocket', '/tmp/dummy') + except stem_utils.InvalidRequest as e: + assert "Unable to set option" in e.message + + +def test_set_torrc_options_can_fail_option_fail(persistent_launch_tor): + controller = persistent_launch_tor + try: + controller.set_conf('BadOption', '0') + except stem_utils.InvalidArguments as e: + assert "Unknown option" in e.message diff -Nru sbws-1.0.2/tests/testnets/.gitignore sbws-1.1.0/tests/testnets/.gitignore --- sbws-1.0.2/tests/testnets/.gitignore 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -auth?/ -relay?/ -exit?/ -client?/ -datadir/ -tor/ -config*.ini -*.log diff -Nru sbws-1.0.2/tests/testnets/run-network.sh sbws-1.1.0/tests/testnets/run-network.sh --- sbws-1.0.2/tests/testnets/run-network.sh 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/run-network.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -set -e -function cleanup { - kill $(jobs -p) - echo -n '' -} -trap cleanup EXIT - -function list_of_nets { - find . -mindepth 2 -maxdepth 2 -type f -name '.net' |\ - xargs dirname | sort -u | xargs -} - -function usage { - echo "Usage: $0 " - echo "Where is one of: $(list_of_nets)" -} - -[ "$1" == "" ] && usage && exit 1 || net="$1" -[ ! -d "$net" ] && usage && exit 1 -[ ! -f "$net/.net" ] && usage && exit 1 - -pushd $net -./01-gen-configs.sh -./02-start-network.sh -sleep 5 -num_relays=$(ls -ld {auth,relay,exit}* | wc -l) -echo "Waiting until network of $num_relays relays is ready ..." -time ./03-network-in-ready-state.py auth* relay* exit* --size $num_relays -echo 'All ready!' - -#sbws -d . server > debug.server.log & -#sleep 1 -sbws -d . scanner > debug.scanner.log & - -run_time="45" -echo "Running for $run_time seconds ..." -sleep $run_time - -sbws -d . generate --output /dev/stdout | tee generate.log -sbws -d . stats | tee stats.log - -./04-stop-network.sh diff -Nru sbws-1.0.2/tests/testnets/simple.common/01-gen-configs.sh sbws-1.1.0/tests/testnets/simple.common/01-gen-configs.sh --- sbws-1.0.2/tests/testnets/simple.common/01-gen-configs.sh 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple.common/01-gen-configs.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,175 +0,0 @@ -#!/usr/bin/env bash -set -e - -which tor || exit 1 -which tor-gencert || exit 1 - -source 00-common.sh - -function get_fingerprint { - dir=$1 - [ -f $dir/torrc ] || exit 2 - tor --ignore-missing-torrc -f $dir/torrc --Address 8.8.8.8 \ - --list-fingerprint | tail -n 1 | cut -d ' ' -f 2- \ - | sed 's|\ ||g' -} - -function get_v3ident { - dir=$1 - cert=$dir/keys/authority_certificate - [ -f $cert ] || exit 2 - grep fingerprint $cert | cut -d ' ' -f 2 -} - - -next_ip="1" -scanner_tor_socks_proxy_ip="" -scanner_tor_socks_proxy_nick="" - -echo -n '' > $auth_torrc_section -rm -fr auth?/ relay?/ exit?/ config*.ini datadir/ tor/ *.log -for A in auth1 auth2 auth3 -do - mkdir -pv $A/keys - chmod 700 $A - ip=${ip_space}${next_ip} - [ "$scanner_tor_socks_proxy_ip" == "" ] && scanner_tor_socks_proxy_ip="$ip" - [ "$scanner_tor_socks_proxy_nick" == "" ] && scanner_tor_socks_proxy_nick="$A" - echo -n '' | tor-gencert --create-identity-key --passphrase-fd 0 -m 24 -a $ip:$dirport - echo " - DataDirectory $A - PidFile $A/tor.pid - Log notice file $A/notice.log - ShutdownWaitLength 2 - ExitRelay 0 - AuthoritativeDirectory 1 - V3AuthoritativeDirectory 1 - Address $ip - SocksPort $ip:$socksport - ControlPort $ip:$controlport - ControlSocket $(pwd)/$A/control_socket - CookieAuthentication 1 - ORPort $ip:$orport - DirPort $ip:$dirport - Nickname $A - ContactInfo pastly@torproject.org - " > $A/torrc - mv -v authority_* $A/keys/ - fp=$(get_fingerprint $A) - v3ident=$(get_v3ident $A) - echo "DirAuthority $A orport=$orport no-v2 v3ident=$v3ident $ip:$dirport $fp" \ - >> $auth_torrc_section - - next_ip=$((next_ip+1)) -done - -for A in relay1 relay2 relay3 relay4 relay5 relay6 relay7 -do - mkdir -pv $A - chmod 700 $A - ip=${ip_space}${next_ip} - echo " - DataDirectory $A - PidFile $A/tor.pid - Log notice file $A/notice.log - ShutdownWaitLength 2 - ExitRelay 0 - Address $ip - SocksPort $ip:$socksport - ControlPort $ip:$controlport - ControlSocket $(pwd)/$A/control_socket - CookieAuthentication 1 - ORPort $ip:$orport - DirPort $ip:$dirport - Nickname $A - ContactInfo pastly@torproject.org - " > $A/torrc - next_ip=$((next_ip+1)) -done - -for A in exit1 exit2 exit3 -do - mkdir -pv $A - chmod 700 $A - ip=${ip_space}${next_ip} - echo " - DataDirectory $A - PidFile $A/tor.pid - Log notice file $A/notice.log - ShutdownWaitLength 2 - ExitRelay 1 - IPv6Exit 1 - ExitPolicy accept *:* - ExitPolicy reject *:* - Address $ip - SocksPort $ip:$socksport - ControlPort $ip:$controlport - ControlSocket $(pwd)/$A/control_socket - CookieAuthentication 1 - ORPort $ip:$orport - DirPort $ip:$dirport - Nickname $A - ContactInfo pastly@torproject.org - " > $A/torrc - next_ip=$((next_ip+1)) -done - -for torrc in ./auth*/torrc -do - echo " - TestingV3AuthInitialVotingInterval 5 - V3AuthVotingInterval 10 - TestingV3AuthInitialVoteDelay 2 - V3AuthVoteDelay 2 - TestingV3AuthInitialDistDelay 2 - V3AuthDistDelay 2 - " >> $torrc -done - -for torrc in ./{auth,relay,exit}*/torrc -do - cat $auth_torrc_section >> $torrc - echo " - TestingTorNetwork 1 - NumCPUs 1 - LogTimeGranularity 1 - SafeLogging 0 - " >> $torrc -done - - -# Get a random port between 2000 and 62000 while handling the fact that $RANDOM -# doesn't go up that high -sbws_server_port=$(( ((RANDOM<<15)|RANDOM) % 60000 + 2000 )) - -echo " -[paths] -sbws_home = $(pwd) - -[tor] -extra_lines = - TestingTorNetwork 1 - NumCPUs 1 - LogTimeGranularity 1 - SafeLogging 0 -$(cat $auth_torrc_section | while read LINE; do printf " $LINE\n"; done) - -[scanner] -nickname = SbwsTestnetScanner -measurement_threads = 4 -download_toofast = 0.1 -download_min = 1 -download_target = 2 -download_max = 5 -num_rtts = 5 -num_downloads = 3 - -[destinations] -debian_cd_mirror_will_break = on - -[destinations.debian_cd_mirror_will_break] -url = https://saimei.ftp.acc.umu.se/debian-cd/9.4.0/amd64/iso-dvd/debian-9.4.0-amd64-DVD-1.iso -#url = https://cdimage.debian.org/debian-cd/9.4.0/amd64/iso-dvd/debian-9.4.0-amd64-DVD-1.iso -" > config.ini -touch config.log.ini -rm $auth_torrc_section diff -Nru sbws-1.0.2/tests/testnets/simple.common/02-start-network.sh sbws-1.1.0/tests/testnets/simple.common/02-start-network.sh --- sbws-1.0.2/tests/testnets/simple.common/02-start-network.sh 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple.common/02-start-network.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -set -e -function cleanup { - #kill -INT $(cat {auth,relay,exit}*/tor.pid) - #kill -INT $(jobs -p) - echo -n '' -} -trap cleanup EXIT - - -for A in {auth,relay,exit}* -do - tor -f $A/torrc --quiet & -done diff -Nru sbws-1.0.2/tests/testnets/simple.common/03-network-in-ready-state.py sbws-1.1.0/tests/testnets/simple.common/03-network-in-ready-state.py --- sbws-1.0.2/tests/testnets/simple.common/03-network-in-ready-state.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple.common/03-network-in-ready-state.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -#!/usr/bin/env python3 -from argparse import RawTextHelpFormatter, ArgumentParser -from stem.control import Controller -import time -import os -import logging - -logger = logging.getLogger(__name__) - - -def get_controller(sock_fname): - cont = Controller.from_socket_file(path=sock_fname) - cont.authenticate() - return cont - - -def get_is_bootstrapped(cont, timeout=60): - start_time = time.time() - while start_time + timeout > time.time(): - line = cont.get_info('status/bootstrap-phase') - state, _, progress, *_ = line.split() - progress = int(progress.split('=')[1]) - if state == 'NOTICE' and progress == 100: - logger.debug('Tor is bootstrapped') - return True - time.sleep(1) - logger.debug('Tor didn\'t bootstrap before timeout. Last line: %s', line) - return False - - -def get_has_full_consensus(cont, network_size, timeout=60): - start_time = time.time() - while start_time + timeout > time.time(): - relays = [r for r in cont.get_network_statuses()] - if len(relays) == network_size: - logger.debug('Tor has correct network size %d', - network_size) - return True - elif len(relays) > network_size: - logger.warning('Tor has more relays than expected. %d vs %d', - len(relays), network_size) - return True - time.sleep(1) - logger.debug('Tor didn\'t reach expected network size %d before ' - 'timeout', network_size) - return False - - -def is_tor_ready(sock_fname, network_size): - with get_controller(sock_fname) as cont: - if not get_is_bootstrapped(cont): - logger.warning('%s not bootstrapped, Tor not ready', sock_fname) - return False - if not get_has_full_consensus(cont, network_size): - logger.warning('%s doesn\'t have full consensus, Tor not ready', - sock_fname) - return False - logger.info('%s is ready', sock_fname) - return True - - -def main(args): - for datadir in args.datadir: - logger.info('Checking if %s is ready', datadir) - sock_fname = os.path.join(datadir, 'control_socket') - assert os.path.exists(sock_fname) - if not is_tor_ready(sock_fname, network_size=args.size): - return 1 - # If we got to this point, it seems like every relay is completely ready. - # Do one more check to make sure that's still the case. - for datadir in args.datadir: - logger.info('Verifying %s is still ready', datadir) - sock_fname = os.path.join(datadir, 'control_socket') - assert os.path.exists(sock_fname) - if not is_tor_ready(sock_fname, network_size=args.size): - return 1 - return 0 - - -if __name__ == '__main__': - desc = ''' -Given the data directories for a local tor network, connect to the control -socket in each directory and verify that the tor on the other end of the socket -is fully bootstrapped and has the right size of consensus. - -The "right size of consensus" is determined based on the number of data -directories given to check. If that is not okay to assume (for example, there -are some Tor client [non-relay] data directories given to check), then specify -the size manually with --size. - -Waits up to 60 seconds for each check for each tor. - -- In the worst case, this script will take a long time to run (if every tor - suddenly passes each check after 59 seconds). -- In the normal failure case, this script will take about 60 seconds to run - (the first tor is not ready and fails its checks). -- In the normal case, it will run very quickly (every tor is bootstrapped and - ready). - -Exits with 0 if everything is good. Otherwise exits with a postive integer. -''' - parser = ArgumentParser( - formatter_class=RawTextHelpFormatter, description=desc) - parser.add_argument('-s', '--size', type=int, help='If given, don\'t ' - 'assume the network size based on the number of ' - 'datadirs, but use this size instead.') - parser.add_argument('-d', '--debug', action='store_true') - parser.add_argument('datadir', nargs='+', type=str) - args = parser.parse_args() - - if args.debug: - logger.setLevel(logging.DEBUG) - else: - logger.setLevel(logging.WARNING) - ch = logging.StreamHandler() - formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') - ch.setFormatter(formatter) - logger.addHandler(ch) - - if not args.size: - args.size = len(args.datadir) - - try: - exit(main(args)) - except KeyboardInterrupt: - pass diff -Nru sbws-1.0.2/tests/testnets/simple.common/04-stop-network.sh sbws-1.1.0/tests/testnets/simple.common/04-stop-network.sh --- sbws-1.0.2/tests/testnets/simple.common/04-stop-network.sh 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple.common/04-stop-network.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -kill -INT $(cat {auth,relay,exit}*/tor.pid) diff -Nru sbws-1.0.2/tests/testnets/simple-ipv4/00-common.sh sbws-1.1.0/tests/testnets/simple-ipv4/00-common.sh --- sbws-1.0.2/tests/testnets/simple-ipv4/00-common.sh 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple-ipv4/00-common.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -ip_space="127.10.0." -socksport="2000" -controlport="2001" -orport="2002" -dirport="2003" -auth_torrc_section="auth.torrc.part" -sbws_server_host="127.0.0.1" diff -Nru sbws-1.0.2/tests/testnets/simple-ipv4/01-gen-configs.sh sbws-1.1.0/tests/testnets/simple-ipv4/01-gen-configs.sh --- sbws-1.0.2/tests/testnets/simple-ipv4/01-gen-configs.sh 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple-ipv4/01-gen-configs.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,175 +0,0 @@ -#!/usr/bin/env bash -set -e - -which tor || exit 1 -which tor-gencert || exit 1 - -source 00-common.sh - -function get_fingerprint { - dir=$1 - [ -f $dir/torrc ] || exit 2 - tor --ignore-missing-torrc -f $dir/torrc --Address 8.8.8.8 \ - --list-fingerprint | tail -n 1 | cut -d ' ' -f 2- \ - | sed 's|\ ||g' -} - -function get_v3ident { - dir=$1 - cert=$dir/keys/authority_certificate - [ -f $cert ] || exit 2 - grep fingerprint $cert | cut -d ' ' -f 2 -} - - -next_ip="1" -scanner_tor_socks_proxy_ip="" -scanner_tor_socks_proxy_nick="" - -echo -n '' > $auth_torrc_section -rm -fr auth?/ relay?/ exit?/ config*.ini datadir/ tor/ *.log -for A in auth1 auth2 auth3 -do - mkdir -pv $A/keys - chmod 700 $A - ip=${ip_space}${next_ip} - [ "$scanner_tor_socks_proxy_ip" == "" ] && scanner_tor_socks_proxy_ip="$ip" - [ "$scanner_tor_socks_proxy_nick" == "" ] && scanner_tor_socks_proxy_nick="$A" - echo -n '' | tor-gencert --create-identity-key --passphrase-fd 0 -m 24 -a $ip:$dirport - echo " - DataDirectory $A - PidFile $A/tor.pid - Log notice file $A/notice.log - ShutdownWaitLength 2 - ExitRelay 0 - AuthoritativeDirectory 1 - V3AuthoritativeDirectory 1 - Address $ip - SocksPort $ip:$socksport - ControlPort $ip:$controlport - ControlSocket $(pwd)/$A/control_socket - CookieAuthentication 1 - ORPort $ip:$orport - DirPort $ip:$dirport - Nickname $A - ContactInfo pastly@torproject.org - " > $A/torrc - mv -v authority_* $A/keys/ - fp=$(get_fingerprint $A) - v3ident=$(get_v3ident $A) - echo "DirAuthority $A orport=$orport no-v2 v3ident=$v3ident $ip:$dirport $fp" \ - >> $auth_torrc_section - - next_ip=$((next_ip+1)) -done - -for A in relay1 relay2 relay3 relay4 relay5 relay6 relay7 -do - mkdir -pv $A - chmod 700 $A - ip=${ip_space}${next_ip} - echo " - DataDirectory $A - PidFile $A/tor.pid - Log notice file $A/notice.log - ShutdownWaitLength 2 - ExitRelay 0 - Address $ip - SocksPort $ip:$socksport - ControlPort $ip:$controlport - ControlSocket $(pwd)/$A/control_socket - CookieAuthentication 1 - ORPort $ip:$orport - DirPort $ip:$dirport - Nickname $A - ContactInfo pastly@torproject.org - " > $A/torrc - next_ip=$((next_ip+1)) -done - -for A in exit1 exit2 exit3 -do - mkdir -pv $A - chmod 700 $A - ip=${ip_space}${next_ip} - echo " - DataDirectory $A - PidFile $A/tor.pid - Log notice file $A/notice.log - ShutdownWaitLength 2 - ExitRelay 1 - IPv6Exit 1 - ExitPolicy accept *:* - ExitPolicy reject *:* - Address $ip - SocksPort $ip:$socksport - ControlPort $ip:$controlport - ControlSocket $(pwd)/$A/control_socket - CookieAuthentication 1 - ORPort $ip:$orport - DirPort $ip:$dirport - Nickname $A - ContactInfo pastly@torproject.org - " > $A/torrc - next_ip=$((next_ip+1)) -done - -for torrc in ./auth*/torrc -do - echo " - TestingV3AuthInitialVotingInterval 5 - V3AuthVotingInterval 10 - TestingV3AuthInitialVoteDelay 2 - V3AuthVoteDelay 2 - TestingV3AuthInitialDistDelay 2 - V3AuthDistDelay 2 - " >> $torrc -done - -for torrc in ./{auth,relay,exit}*/torrc -do - cat $auth_torrc_section >> $torrc - echo " - TestingTorNetwork 1 - NumCPUs 1 - LogTimeGranularity 1 - SafeLogging 0 - " >> $torrc -done - - -# Get a random port between 2000 and 62000 while handling the fact that $RANDOM -# doesn't go up that high -sbws_server_port=$(( ((RANDOM<<15)|RANDOM) % 60000 + 2000 )) - -echo " -[paths] -sbws_home = $(pwd) - -[tor] -extra_lines = - TestingTorNetwork 1 - NumCPUs 1 - LogTimeGranularity 1 - SafeLogging 0 -$(cat $auth_torrc_section | while read LINE; do printf " $LINE\n"; done) - -[scanner] -nickname = SbwsTestnetScanner -measurement_threads = 4 -download_toofast = 0.1 -download_min = 1 -download_target = 2 -download_max = 5 -num_rtts = 5 -num_downloads = 3 - -[destinations] -debian_cd_mirror_will_break = on - -[destinations.debian_cd_mirror_will_break] -url = https://saimei.ftp.acc.umu.se/debian-cd/9.4.0/amd64/iso-dvd/debian-9.4.0-amd64-DVD-1.iso -#url = https://cdimage.debian.org/debian-cd/9.4.0/amd64/iso-dvd/debian-9.4.0-amd64-DVD-1.iso -" > config.ini -touch config.log.ini -rm $auth_torrc_section diff -Nru sbws-1.0.2/tests/testnets/simple-ipv4/02-start-network.sh sbws-1.1.0/tests/testnets/simple-ipv4/02-start-network.sh --- sbws-1.0.2/tests/testnets/simple-ipv4/02-start-network.sh 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple-ipv4/02-start-network.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -set -e -function cleanup { - #kill -INT $(cat {auth,relay,exit}*/tor.pid) - #kill -INT $(jobs -p) - echo -n '' -} -trap cleanup EXIT - - -for A in {auth,relay,exit}* -do - tor -f $A/torrc --quiet & -done diff -Nru sbws-1.0.2/tests/testnets/simple-ipv4/03-network-in-ready-state.py sbws-1.1.0/tests/testnets/simple-ipv4/03-network-in-ready-state.py --- sbws-1.0.2/tests/testnets/simple-ipv4/03-network-in-ready-state.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple-ipv4/03-network-in-ready-state.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -#!/usr/bin/env python3 -from argparse import RawTextHelpFormatter, ArgumentParser -from stem.control import Controller -import time -import os -import logging - -logger = logging.getLogger(__name__) - - -def get_controller(sock_fname): - cont = Controller.from_socket_file(path=sock_fname) - cont.authenticate() - return cont - - -def get_is_bootstrapped(cont, timeout=60): - start_time = time.time() - while start_time + timeout > time.time(): - line = cont.get_info('status/bootstrap-phase') - state, _, progress, *_ = line.split() - progress = int(progress.split('=')[1]) - if state == 'NOTICE' and progress == 100: - logger.debug('Tor is bootstrapped') - return True - time.sleep(1) - logger.debug('Tor didn\'t bootstrap before timeout. Last line: %s', line) - return False - - -def get_has_full_consensus(cont, network_size, timeout=60): - start_time = time.time() - while start_time + timeout > time.time(): - relays = [r for r in cont.get_network_statuses()] - if len(relays) == network_size: - logger.debug('Tor has correct network size %d', - network_size) - return True - elif len(relays) > network_size: - logger.warning('Tor has more relays than expected. %d vs %d', - len(relays), network_size) - return True - time.sleep(1) - logger.debug('Tor didn\'t reach expected network size %d before ' - 'timeout', network_size) - return False - - -def is_tor_ready(sock_fname, network_size): - with get_controller(sock_fname) as cont: - if not get_is_bootstrapped(cont): - logger.warning('%s not bootstrapped, Tor not ready', sock_fname) - return False - if not get_has_full_consensus(cont, network_size): - logger.warning('%s doesn\'t have full consensus, Tor not ready', - sock_fname) - return False - logger.info('%s is ready', sock_fname) - return True - - -def main(args): - for datadir in args.datadir: - logger.info('Checking if %s is ready', datadir) - sock_fname = os.path.join(datadir, 'control_socket') - assert os.path.exists(sock_fname) - if not is_tor_ready(sock_fname, network_size=args.size): - return 1 - # If we got to this point, it seems like every relay is completely ready. - # Do one more check to make sure that's still the case. - for datadir in args.datadir: - logger.info('Verifying %s is still ready', datadir) - sock_fname = os.path.join(datadir, 'control_socket') - assert os.path.exists(sock_fname) - if not is_tor_ready(sock_fname, network_size=args.size): - return 1 - return 0 - - -if __name__ == '__main__': - desc = ''' -Given the data directories for a local tor network, connect to the control -socket in each directory and verify that the tor on the other end of the socket -is fully bootstrapped and has the right size of consensus. - -The "right size of consensus" is determined based on the number of data -directories given to check. If that is not okay to assume (for example, there -are some Tor client [non-relay] data directories given to check), then specify -the size manually with --size. - -Waits up to 60 seconds for each check for each tor. - -- In the worst case, this script will take a long time to run (if every tor - suddenly passes each check after 59 seconds). -- In the normal failure case, this script will take about 60 seconds to run - (the first tor is not ready and fails its checks). -- In the normal case, it will run very quickly (every tor is bootstrapped and - ready). - -Exits with 0 if everything is good. Otherwise exits with a postive integer. -''' - parser = ArgumentParser( - formatter_class=RawTextHelpFormatter, description=desc) - parser.add_argument('-s', '--size', type=int, help='If given, don\'t ' - 'assume the network size based on the number of ' - 'datadirs, but use this size instead.') - parser.add_argument('-d', '--debug', action='store_true') - parser.add_argument('datadir', nargs='+', type=str) - args = parser.parse_args() - - if args.debug: - logger.setLevel(logging.DEBUG) - else: - logger.setLevel(logging.WARNING) - ch = logging.StreamHandler() - formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') - ch.setFormatter(formatter) - logger.addHandler(ch) - - if not args.size: - args.size = len(args.datadir) - - try: - exit(main(args)) - except KeyboardInterrupt: - pass diff -Nru sbws-1.0.2/tests/testnets/simple-ipv4/04-stop-network.sh sbws-1.1.0/tests/testnets/simple-ipv4/04-stop-network.sh --- sbws-1.0.2/tests/testnets/simple-ipv4/04-stop-network.sh 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple-ipv4/04-stop-network.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -kill -INT $(cat {auth,relay,exit}*/tor.pid) diff -Nru sbws-1.0.2/tests/testnets/simple-ipv4/html/alphabet.txt sbws-1.1.0/tests/testnets/simple-ipv4/html/alphabet.txt --- sbws-1.0.2/tests/testnets/simple-ipv4/html/alphabet.txt 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple-ipv4/html/alphabet.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -abcdefghijklmnopqrstuvwxyz \ No newline at end of file diff -Nru sbws-1.0.2/tests/testnets/simple-ipv6/00-common.sh sbws-1.1.0/tests/testnets/simple-ipv6/00-common.sh --- sbws-1.0.2/tests/testnets/simple-ipv6/00-common.sh 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple-ipv6/00-common.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -ip_space="127.10.0." -socksport="2000" -controlport="2001" -orport="2002" -dirport="2003" -auth_torrc_section="auth.torrc.part" -sbws_server_host="::1" diff -Nru sbws-1.0.2/tests/testnets/simple-ipv6/01-gen-configs.sh sbws-1.1.0/tests/testnets/simple-ipv6/01-gen-configs.sh --- sbws-1.0.2/tests/testnets/simple-ipv6/01-gen-configs.sh 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple-ipv6/01-gen-configs.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,175 +0,0 @@ -#!/usr/bin/env bash -set -e - -which tor || exit 1 -which tor-gencert || exit 1 - -source 00-common.sh - -function get_fingerprint { - dir=$1 - [ -f $dir/torrc ] || exit 2 - tor --ignore-missing-torrc -f $dir/torrc --Address 8.8.8.8 \ - --list-fingerprint | tail -n 1 | cut -d ' ' -f 2- \ - | sed 's|\ ||g' -} - -function get_v3ident { - dir=$1 - cert=$dir/keys/authority_certificate - [ -f $cert ] || exit 2 - grep fingerprint $cert | cut -d ' ' -f 2 -} - - -next_ip="1" -scanner_tor_socks_proxy_ip="" -scanner_tor_socks_proxy_nick="" - -echo -n '' > $auth_torrc_section -rm -fr auth?/ relay?/ exit?/ config*.ini datadir/ tor/ *.log -for A in auth1 auth2 auth3 -do - mkdir -pv $A/keys - chmod 700 $A - ip=${ip_space}${next_ip} - [ "$scanner_tor_socks_proxy_ip" == "" ] && scanner_tor_socks_proxy_ip="$ip" - [ "$scanner_tor_socks_proxy_nick" == "" ] && scanner_tor_socks_proxy_nick="$A" - echo -n '' | tor-gencert --create-identity-key --passphrase-fd 0 -m 24 -a $ip:$dirport - echo " - DataDirectory $A - PidFile $A/tor.pid - Log notice file $A/notice.log - ShutdownWaitLength 2 - ExitRelay 0 - AuthoritativeDirectory 1 - V3AuthoritativeDirectory 1 - Address $ip - SocksPort $ip:$socksport - ControlPort $ip:$controlport - ControlSocket $(pwd)/$A/control_socket - CookieAuthentication 1 - ORPort $ip:$orport - DirPort $ip:$dirport - Nickname $A - ContactInfo pastly@torproject.org - " > $A/torrc - mv -v authority_* $A/keys/ - fp=$(get_fingerprint $A) - v3ident=$(get_v3ident $A) - echo "DirAuthority $A orport=$orport no-v2 v3ident=$v3ident $ip:$dirport $fp" \ - >> $auth_torrc_section - - next_ip=$((next_ip+1)) -done - -for A in relay1 relay2 relay3 relay4 relay5 relay6 relay7 -do - mkdir -pv $A - chmod 700 $A - ip=${ip_space}${next_ip} - echo " - DataDirectory $A - PidFile $A/tor.pid - Log notice file $A/notice.log - ShutdownWaitLength 2 - ExitRelay 0 - Address $ip - SocksPort $ip:$socksport - ControlPort $ip:$controlport - ControlSocket $(pwd)/$A/control_socket - CookieAuthentication 1 - ORPort $ip:$orport - DirPort $ip:$dirport - Nickname $A - ContactInfo pastly@torproject.org - " > $A/torrc - next_ip=$((next_ip+1)) -done - -for A in exit1 exit2 exit3 -do - mkdir -pv $A - chmod 700 $A - ip=${ip_space}${next_ip} - echo " - DataDirectory $A - PidFile $A/tor.pid - Log notice file $A/notice.log - ShutdownWaitLength 2 - ExitRelay 1 - IPv6Exit 1 - ExitPolicy accept *:* - ExitPolicy reject *:* - Address $ip - SocksPort $ip:$socksport - ControlPort $ip:$controlport - ControlSocket $(pwd)/$A/control_socket - CookieAuthentication 1 - ORPort $ip:$orport - DirPort $ip:$dirport - Nickname $A - ContactInfo pastly@torproject.org - " > $A/torrc - next_ip=$((next_ip+1)) -done - -for torrc in ./auth*/torrc -do - echo " - TestingV3AuthInitialVotingInterval 5 - V3AuthVotingInterval 10 - TestingV3AuthInitialVoteDelay 2 - V3AuthVoteDelay 2 - TestingV3AuthInitialDistDelay 2 - V3AuthDistDelay 2 - " >> $torrc -done - -for torrc in ./{auth,relay,exit}*/torrc -do - cat $auth_torrc_section >> $torrc - echo " - TestingTorNetwork 1 - NumCPUs 1 - LogTimeGranularity 1 - SafeLogging 0 - " >> $torrc -done - - -# Get a random port between 2000 and 62000 while handling the fact that $RANDOM -# doesn't go up that high -sbws_server_port=$(( ((RANDOM<<15)|RANDOM) % 60000 + 2000 )) - -echo " -[paths] -sbws_home = $(pwd) - -[tor] -extra_lines = - TestingTorNetwork 1 - NumCPUs 1 - LogTimeGranularity 1 - SafeLogging 0 -$(cat $auth_torrc_section | while read LINE; do printf " $LINE\n"; done) - -[scanner] -nickname = SbwsTestnetScanner -measurement_threads = 4 -download_toofast = 0.1 -download_min = 1 -download_target = 2 -download_max = 5 -num_rtts = 5 -num_downloads = 3 - -[destinations] -debian_cd_mirror_will_break = on - -[destinations.debian_cd_mirror_will_break] -url = https://saimei.ftp.acc.umu.se/debian-cd/9.4.0/amd64/iso-dvd/debian-9.4.0-amd64-DVD-1.iso -#url = https://cdimage.debian.org/debian-cd/9.4.0/amd64/iso-dvd/debian-9.4.0-amd64-DVD-1.iso -" > config.ini -touch config.log.ini -rm $auth_torrc_section diff -Nru sbws-1.0.2/tests/testnets/simple-ipv6/02-start-network.sh sbws-1.1.0/tests/testnets/simple-ipv6/02-start-network.sh --- sbws-1.0.2/tests/testnets/simple-ipv6/02-start-network.sh 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple-ipv6/02-start-network.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -set -e -function cleanup { - #kill -INT $(cat {auth,relay,exit}*/tor.pid) - #kill -INT $(jobs -p) - echo -n '' -} -trap cleanup EXIT - - -for A in {auth,relay,exit}* -do - tor -f $A/torrc --quiet & -done diff -Nru sbws-1.0.2/tests/testnets/simple-ipv6/03-network-in-ready-state.py sbws-1.1.0/tests/testnets/simple-ipv6/03-network-in-ready-state.py --- sbws-1.0.2/tests/testnets/simple-ipv6/03-network-in-ready-state.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple-ipv6/03-network-in-ready-state.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -#!/usr/bin/env python3 -from argparse import RawTextHelpFormatter, ArgumentParser -from stem.control import Controller -import time -import os -import logging - -logger = logging.getLogger(__name__) - - -def get_controller(sock_fname): - cont = Controller.from_socket_file(path=sock_fname) - cont.authenticate() - return cont - - -def get_is_bootstrapped(cont, timeout=60): - start_time = time.time() - while start_time + timeout > time.time(): - line = cont.get_info('status/bootstrap-phase') - state, _, progress, *_ = line.split() - progress = int(progress.split('=')[1]) - if state == 'NOTICE' and progress == 100: - logger.debug('Tor is bootstrapped') - return True - time.sleep(1) - logger.debug('Tor didn\'t bootstrap before timeout. Last line: %s', line) - return False - - -def get_has_full_consensus(cont, network_size, timeout=60): - start_time = time.time() - while start_time + timeout > time.time(): - relays = [r for r in cont.get_network_statuses()] - if len(relays) == network_size: - logger.debug('Tor has correct network size %d', - network_size) - return True - elif len(relays) > network_size: - logger.warning('Tor has more relays than expected. %d vs %d', - len(relays), network_size) - return True - time.sleep(1) - logger.debug('Tor didn\'t reach expected network size %d before ' - 'timeout', network_size) - return False - - -def is_tor_ready(sock_fname, network_size): - with get_controller(sock_fname) as cont: - if not get_is_bootstrapped(cont): - logger.warning('%s not bootstrapped, Tor not ready', sock_fname) - return False - if not get_has_full_consensus(cont, network_size): - logger.warning('%s doesn\'t have full consensus, Tor not ready', - sock_fname) - return False - logger.info('%s is ready', sock_fname) - return True - - -def main(args): - for datadir in args.datadir: - logger.info('Checking if %s is ready', datadir) - sock_fname = os.path.join(datadir, 'control_socket') - assert os.path.exists(sock_fname) - if not is_tor_ready(sock_fname, network_size=args.size): - return 1 - # If we got to this point, it seems like every relay is completely ready. - # Do one more check to make sure that's still the case. - for datadir in args.datadir: - logger.info('Verifying %s is still ready', datadir) - sock_fname = os.path.join(datadir, 'control_socket') - assert os.path.exists(sock_fname) - if not is_tor_ready(sock_fname, network_size=args.size): - return 1 - return 0 - - -if __name__ == '__main__': - desc = ''' -Given the data directories for a local tor network, connect to the control -socket in each directory and verify that the tor on the other end of the socket -is fully bootstrapped and has the right size of consensus. - -The "right size of consensus" is determined based on the number of data -directories given to check. If that is not okay to assume (for example, there -are some Tor client [non-relay] data directories given to check), then specify -the size manually with --size. - -Waits up to 60 seconds for each check for each tor. - -- In the worst case, this script will take a long time to run (if every tor - suddenly passes each check after 59 seconds). -- In the normal failure case, this script will take about 60 seconds to run - (the first tor is not ready and fails its checks). -- In the normal case, it will run very quickly (every tor is bootstrapped and - ready). - -Exits with 0 if everything is good. Otherwise exits with a postive integer. -''' - parser = ArgumentParser( - formatter_class=RawTextHelpFormatter, description=desc) - parser.add_argument('-s', '--size', type=int, help='If given, don\'t ' - 'assume the network size based on the number of ' - 'datadirs, but use this size instead.') - parser.add_argument('-d', '--debug', action='store_true') - parser.add_argument('datadir', nargs='+', type=str) - args = parser.parse_args() - - if args.debug: - logger.setLevel(logging.DEBUG) - else: - logger.setLevel(logging.WARNING) - ch = logging.StreamHandler() - formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') - ch.setFormatter(formatter) - logger.addHandler(ch) - - if not args.size: - args.size = len(args.datadir) - - try: - exit(main(args)) - except KeyboardInterrupt: - pass diff -Nru sbws-1.0.2/tests/testnets/simple-ipv6/04-stop-network.sh sbws-1.1.0/tests/testnets/simple-ipv6/04-stop-network.sh --- sbws-1.0.2/tests/testnets/simple-ipv6/04-stop-network.sh 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/testnets/simple-ipv6/04-stop-network.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -kill -INT $(cat {auth,relay,exit}*/tor.pid) diff -Nru sbws-1.0.2/tests/unit/conftest.py sbws-1.1.0/tests/unit/conftest.py --- sbws-1.0.2/tests/unit/conftest.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/unit/conftest.py 2019-03-29 13:47:26.000000000 +0000 @@ -63,10 +63,16 @@ ] SCANNER = "test" AVG_BW = 966080 +BUR_BW = 1048576 OBS_BW = 524288 +BW = 600000 +UNMEASURED = False RELAY1 = Result.Relay(FP1, NICK1, IP1, ED25519, - average_bandwidth=AVG_BW, observed_bandwidth=OBS_BW) + average_bandwidth=AVG_BW, burst_bandwidth=BUR_BW, + observed_bandwidth=OBS_BW, consensus_bandwidth=BW, + consensus_bandwidth_is_unmeasured=UNMEASURED, + relay_in_recent_consensus_count=2) RELAY2 = Result.Relay(FP2, NICK2, IP2, ED25519) RESULT = Result(RELAY1, CIRC12, DEST_URL, SCANNER, t=TIME1) @@ -86,7 +92,11 @@ "nickname": NICK1, "master_key_ed25519": ED25519, "relay_average_bandwidth": AVG_BW, - "relay_observed_bandwidth": OBS_BW + "relay_burst_bandwidth": BUR_BW, + "relay_observed_bandwidth": OBS_BW, + "consensus_bandwidth": BW, + "consensus_bandwidth_is_unmeasured": UNMEASURED, + "relay_in_recent_consensus_count": 2, } BASE_RESULT_NO_RELAY_DICT = { @@ -169,10 +179,11 @@ @pytest.fixture(scope='function') -def conf(sbwshome_empty): +def conf(sbwshome_empty, tmpdir): """Default configuration with sbws home in the tmp test dir.""" conf = _get_default_config() conf['paths']['sbws_home'] = sbwshome_empty + conf['paths']['state_fpath'] = str(tmpdir.join('.sbws', 'state.dat')) return conf @@ -259,3 +270,23 @@ write_result_to_datadir(RESULT_SUCCESS2, dd) write_result_to_datadir(RESULT_SUCCESS2, dd) return sbwshome_only_datadir + + +@pytest.fixture(scope='function') +def end_event(): + import threading + return threading.Event() + + +@pytest.fixture(scope='function') +def rd(args, conf, end_event): + from sbws.lib.resultdump import ResultDump + # in Travis the next line gives the error: + # TypeError: __init__() takes 3 positional arguments but 4 were given + # No idea why. + # Returning None to disable the test in case ResultDump can not be + # initialized. + try: + return ResultDump(args, conf, end_event) + except TypeError: + return None diff -Nru sbws-1.0.2/tests/unit/core/test_generate.py sbws-1.1.0/tests/unit/core/test_generate.py --- sbws-1.0.2/tests/unit/core/test_generate.py 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/unit/core/test_generate.py 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,34 @@ +"""Unit tests for sbws.core.generate module.""" +import argparse + +from sbws.globals import TORFLOW_ROUND_DIG, PROP276_ROUND_DIG +from sbws.core.generate import gen_parser + + +def test_gen_parser_arg_round_digs(): + """ + Test that both --torflow-round-digs and --round-digs arguments can be + passed and round-digs is PROP276_ROUND_DIG by default. + + """ + parent_parser = argparse.ArgumentParser(prog='sbws') + subparsers = parent_parser.add_subparsers(help='generate help') + parser_generate = gen_parser(subparsers) + # Explicitely set empty arguments, otherwise pytest will use pytest + # arguments + args = parser_generate.parse_args([]) + assert args.round_digs == PROP276_ROUND_DIG + # torflow_round_digs is not in the Namespace + assert getattr(args, 'torflow_round_digs', None) is None + # but it can still be passed as an argument + args = parser_generate.parse_args(['--torflow-round-digs', + str(TORFLOW_ROUND_DIG)]) + # though the variable is named round_digs + assert args.round_digs == TORFLOW_ROUND_DIG + # or use the short version + args = parser_generate.parse_args(['-r', str(TORFLOW_ROUND_DIG)]) + assert args.round_digs == TORFLOW_ROUND_DIG + # or use round-digs explicitely + args = parser_generate.parse_args(['--round-digs', + str(PROP276_ROUND_DIG)]) + assert args.round_digs == PROP276_ROUND_DIG diff -Nru sbws-1.0.2/tests/unit/core/test_scanner.py sbws-1.1.0/tests/unit/core/test_scanner.py --- sbws-1.0.2/tests/unit/core/test_scanner.py 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/unit/core/test_scanner.py 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,28 @@ +"""Unit tests for scanner.py.""" +import pytest + +from sbws.core.scanner import result_putter + + +def test_result_putter(sbwshome_only_datadir, result_success, rd, end_event): + if rd is None: + pytest.skip("ResultDump is None") + # Put one item in the queue + callback = result_putter(rd) + callback(result_success) + assert rd.queue.qsize() == 1 + + # Make queue maxsize 1, so that it'll be full after the first callback. + # The second callback will wait 1 second, then the queue will be empty + # again. + rd.queue.maxsize = 1 + callback(result_success) + # after putting 1 result, the queue will be full + assert rd.queue.qsize() == 1 + assert rd.queue.full() + # it's still possible to put another results, because the callback will + # wait 1 second and the queue will be empty again. + callback(result_success) + assert rd.queue.qsize() == 1 + assert rd.queue.full() + end_event.set() diff -Nru sbws-1.0.2/tests/unit/lib/data/results_away.txt sbws-1.1.0/tests/unit/lib/data/results_away.txt --- sbws-1.0.2/tests/unit/lib/data/results_away.txt 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/unit/lib/data/results_away.txt 2019-03-29 13:47:26.000000000 +0000 @@ -1,7 +1,7 @@ -{"version": 4, "time": 1523887747, "circ": ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "A", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_observed_bandwidth": 524288} +{"version": 4, "time": 1523887747, "circ": ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "A", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_burst_bandwidth": 1000000000, "relay_observed_bandwidth": 524288, "consensus_bandwidth": 600000, "consensus_bandwidth_is_unmeasured": false} {"version": 4, "time": 1523974147, "circ": ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"], "type": "error-stream", "msg": "Something bad happened while measuring bandwidth", "fingerprint": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "scanner": "IDidntEditTheSBWSConfig", "dest_url": "http://y.z", "nickname": "A", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s"} -{"version": 4, "time": 1523975587, "circ": ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "A", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_observed_bandwidth": 524288} -{"version": 4, "time": 1524019507, "circ": ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "A", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_observed_bandwidth": 524288} -{"version": 4, "time": 1523975587, "circ": ["BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "B", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_observed_bandwidth": 524288} -{"version": 4, "time": 1524019507, "circ": ["BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "B", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_observed_bandwidth": 524288} -{"version": 4, "time": 1524019507, "circ": ["CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "C", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_observed_bandwidth": 524288} +{"version": 4, "time": 1523975587, "circ": ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "A", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_burst_bandwidth": 1000000000, "relay_observed_bandwidth": 524288, "consensus_bandwidth": 600000, "consensus_bandwidth_is_unmeasured": false} +{"version": 4, "time": 1524019507, "circ": ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "A", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_burst_bandwidth": 1000000000, "relay_observed_bandwidth": 524288, "consensus_bandwidth": 600000, "consensus_bandwidth_is_unmeasured": false} +{"version": 4, "time": 1523975587, "circ": ["BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "B", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_burst_bandwidth": 1000000000, "relay_observed_bandwidth": 524288, "consensus_bandwidth": 600000, "consensus_bandwidth_is_unmeasured": false} +{"version": 4, "time": 1524019507, "circ": ["BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "B", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_burst_bandwidth": 1000000000, "relay_observed_bandwidth": 524288, "consensus_bandwidth": 600000, "consensus_bandwidth_is_unmeasured": false} +{"version": 4, "time": 1524019507, "circ": ["CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "C", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_burst_bandwidth": 1000000000, "relay_observed_bandwidth": 524288, "consensus_bandwidth": 600000, "consensus_bandwidth_is_unmeasured": false} diff -Nru sbws-1.0.2/tests/unit/lib/data/results.txt sbws-1.1.0/tests/unit/lib/data/results.txt --- sbws-1.0.2/tests/unit/lib/data/results.txt 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/unit/lib/data/results.txt 2019-03-29 13:47:26.000000000 +0000 @@ -1,2 +1,2 @@ -{"version": 4, "time": 1523887747, "circ": ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "A", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_observed_bandwidth": 524288} -{"version": 4, "time": 1523974147, "circ": ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"], "type": "error-stream", "msg": "Something bad happened while measuring bandwidth", "fingerprint": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "scanner": "IDidntEditTheSBWSConfig", "dest_url": "http://y.z", "nickname": "A", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s"} +{"version": 4, "time": 1523887747, "circ": ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"], "type": "success", "rtts": [0.4596822261810303, 0.44872617721557617, 0.4563450813293457, 0.44872212409973145, 0.4561030864715576, 0.4765200614929199, 0.4495084285736084, 0.45711588859558105, 0.45520496368408203, 0.4635589122772217], "fingerprint": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "scanner": "IDidntEditTheSBWSConfig", "downloads": [{"amount": 590009, "duration": 6.1014368534088135}, {"amount": 590009, "duration": 8.391342878341675}, {"amount": 321663, "duration": 7.064587831497192}, {"amount": 321663, "duration": 8.266003131866455}, {"amount": 321663, "duration": 5.779450178146362}], "dest_url": "http://y.z", "nickname": "A", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_average_bandwidth": 1000000000, "relay_burst_bandwidth": 123456, "relay_observed_bandwidth": 524288, "consensus_bandwidth": 600000, "consensus_bandwidth_is_unmeasured": false, "relay_recent_measurement_attempt_count": 1, "relay_recent_priority_list_count": 3} +{"version": 4, "time": 1523974147, "circ": ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"], "type": "error-stream", "msg": "Something bad happened while measuring bandwidth", "fingerprint": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "scanner": "IDidntEditTheSBWSConfig", "dest_url": "http://y.z", "nickname": "A", "address": "111.111.111.111", "master_key_ed25519": "g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s", "relay_recent_measurement_attempt_count": 2, "relay_recent_priority_list_count": 3} diff -Nru sbws-1.0.2/tests/unit/lib/test_destination.py sbws-1.1.0/tests/unit/lib/test_destination.py --- sbws-1.0.2/tests/unit/lib/test_destination.py 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/unit/lib/test_destination.py 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,54 @@ +"""Unit tests for sbws.lib.destination.""" +from datetime import datetime, timedelta + +from sbws.lib import destination + + +def test_destination_is_functional(): + eleven_mins_ago = datetime.utcnow() - timedelta(minutes=11) + six_mins_ago = datetime.utcnow() - timedelta(minutes=6) + four_mins_ago = datetime.utcnow() - timedelta(minutes=4) + + d = destination.Destination('unexistenturl', 0, False) + assert d.is_functional() + + # Fail 3 consecutive times + d.add_failure() + d.add_failure() + d.add_failure() + assert d._are_last_attempts_failures() + assert not d._is_last_try_old_enough() + assert not d.is_functional() + + # Then doesn't fail and it's functional again + d.add_success() + assert not d._are_last_attempts_failures() + assert d.is_functional() + + # Fail again 3 times + d.add_failure() + d.add_failure() + # And last failure was 2h ago + d.add_failure(four_mins_ago) + assert d._are_last_attempts_failures() + assert not d._is_last_try_old_enough() + assert not d.is_functional() + + # But if the last failure was 4h ago, try to use it again + # And last failure was 4h ago + d.add_failure(six_mins_ago) + assert d._is_last_try_old_enough() + assert d.is_functional() + + # If last failure was 8h ago, try to use it again again + d.add_failure(eleven_mins_ago) + assert d._is_last_try_old_enough() + assert d.is_functional() + + # Whenever it does not fail again, reset the time to try again + # on 3 consecutive failures + d.add_success() + assert not d._are_last_attempts_failures() + assert d.is_functional() + # And the delta to try is resetted + assert not d._is_last_try_old_enough() diff -Nru sbws-1.0.2/tests/unit/lib/test_heartbeat.py sbws-1.1.0/tests/unit/lib/test_heartbeat.py --- sbws-1.0.2/tests/unit/lib/test_heartbeat.py 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/unit/lib/test_heartbeat.py 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,23 @@ +"""Unit tests for heartbeat""" +import logging + +from sbws.lib import heartbeat + + +def test_total_measured_percent(conf, caplog): + hbeat = heartbeat.Heartbeat(conf.getpath('paths', 'state_fname')) + + hbeat.register_consensus_fprs(['A', 'B', 'C']) + + hbeat.register_measured_fpr('A') + hbeat.register_measured_fpr('B') + + caplog.set_level(logging.INFO) + + assert hbeat.previous_measurement_percent == 0 + + hbeat.print_heartbeat_message() + + assert hbeat.previous_measurement_percent == 67 + caplog.records[1].getMessage().find("Measured in total 2 (67%)") + caplog.records[2].getMessage().find("1 relays still not measured") diff -Nru sbws-1.0.2/tests/unit/lib/test_v3bwfile.py sbws-1.1.0/tests/unit/lib/test_v3bwfile.py --- sbws-1.0.2/tests/unit/lib/test_v3bwfile.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/unit/lib/test_v3bwfile.py 2019-03-29 13:47:26.000000000 +0000 @@ -1,20 +1,31 @@ # -*- coding: utf-8 -*- """Test generation of bandwidth measurements document (v3bw)""" import json +import logging +import math import os.path +from unittest import mock from sbws import __version__ as version from sbws.globals import (SPEC_VERSION, SBWS_SCALING, TORFLOW_SCALING, - MIN_REPORT) + MIN_REPORT, TORFLOW_ROUND_DIG, PROP276_ROUND_DIG) from sbws.lib.resultdump import Result, load_result_file, ResultSuccess -from sbws.lib.v3bwfile import (V3BWHeader, V3BWLine, TERMINATOR, LINE_SEP, - KEYVALUE_SEP_V1, num_results_of_type, - V3BWFile) +from sbws.lib.v3bwfile import ( + V3BWHeader, V3BWLine, TERMINATOR, LINE_SEP, + KEYVALUE_SEP_V1, num_results_of_type, + V3BWFile, round_sig_dig, + BW_HEADER_KEYVALUES_RECENT_MEASUREMENTS_EXCLUDED + ) from sbws.util.timestamp import now_fname, now_isodt_str, now_unixts timestamp = 1523974147 timestamp_l = str(timestamp) version_l = KEYVALUE_SEP_V1.join(['version', SPEC_VERSION]) +scanner_country = 'US' +scanner_country_l = KEYVALUE_SEP_V1.join(['scanner_country', scanner_country]) +destinations_countries = '00,DE' +destinations_countries_l = KEYVALUE_SEP_V1.join(['destinations_countries', + destinations_countries]) software_l = KEYVALUE_SEP_V1.join(['software', 'sbws']) software_version_l = KEYVALUE_SEP_V1.join(['software_version', version]) file_created = '2018-04-25T13:10:57' @@ -22,8 +33,16 @@ latest_bandwidth = '2018-04-17T14:09:07' latest_bandwidth_l = KEYVALUE_SEP_V1.join(['latest_bandwidth', latest_bandwidth]) -header_ls = [timestamp_l, version_l, file_created_l, latest_bandwidth_l, - software_l, software_version_l, TERMINATOR] +attempts = '1' +attempts_l = KEYVALUE_SEP_V1.join(['recent_measurement_attempt_count', + attempts]) +failure = '0' +failure_l = KEYVALUE_SEP_V1.join(['recent_measurement_failure_count', + failure]) +header_ls = [timestamp_l, version_l, destinations_countries_l, file_created_l, + latest_bandwidth_l, + # attempts_l, failure_l, + scanner_country_l, software_l, software_version_l, TERMINATOR] header_str = LINE_SEP.join(header_ls) + LINE_SEP earliest_bandwidth = '2018-04-16T14:09:07' earliest_bandwidth_l = KEYVALUE_SEP_V1.join(['earliest_bandwidth', @@ -37,20 +56,32 @@ software_l, software_version_l, TERMINATOR] header_extra_str = LINE_SEP.join(header_extra_ls) + LINE_SEP -bwl_str = "bw=56 bw_mean=61423 bw_median=55656 "\ - "desc_bw_avg=1000000000 desc_bw_obs_last=524288 "\ - "desc_bw_obs_mean=524288 error_circ=0 error_misc=0 error_stream=1 " \ +# Line produced without any scaling. +# unmeasured and vote are not congruent with the exclusion, +# but `from_data` is only used in the test and doesn't include the +# arg `min_num` +raw_bwl_str = "bw=56 bw_mean=61423 bw_median=55656 "\ + "consensus_bandwidth=600000 consensus_bandwidth_is_unmeasured=False "\ + "desc_bw_avg=1000000000 desc_bw_bur=123456 desc_bw_obs_last=524288 "\ + "desc_bw_obs_mean=524288 error_circ=0 error_destination=0 error_misc=0 " \ + "error_second_relay=0 error_stream=1 " \ "master_key_ed25519=g+Shk00y9Md0hg1S6ptnuc/wWKbADBgdjT0Kg+TSF3s " \ "nick=A " \ - "node_id=$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA rtt=456 success=1 " \ + "node_id=$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA "\ + "relay_recent_measurement_attempt_count=2 "\ + "relay_recent_measurements_excluded_error_count=1 "\ + "relay_recent_priority_list_count=3 "\ + "rtt=456 success=1 " \ "time=2018-04-17T14:09:07\n" -v3bw_str = header_extra_str + bwl_str +v3bw_str = header_extra_str + raw_bwl_str def test_v3bwheader_str(): """Test header str""" - header = V3BWHeader(timestamp_l, file_created=file_created) + header = V3BWHeader(timestamp_l, scanner_country=scanner_country, + destinations_countries=destinations_countries, + file_created=file_created) assert header_str == str(header) @@ -64,7 +95,6 @@ def test_v3bwheader_from_lines(): - """""" header_obj = V3BWHeader(timestamp_l, file_created=file_created, generator_started=generator_started, @@ -74,7 +104,6 @@ def test_v3bwheader_from_text(): - """""" header_obj = V3BWHeader(timestamp_l, file_created=file_created, generator_started=generator_started, @@ -90,6 +119,124 @@ assert num_results_of_type([result_error_stream], 'error-stream') == 1 +def assert_round_sig_dig_any_digits(n, result): + """Test that rounding n to any reasonable number of significant digits + produces result.""" + max_digits_int64 = int(math.ceil(math.log10(2**64 - 1))) + 1 + for d in range(1, max_digits_int64 + 1): + assert(round_sig_dig(n, digits=d) == result) + + +def assert_round_sig_dig_any_digits_error(n, elp_fraction=0.5): + """Test that rounding n to any reasonable number of significant digits + produces a result within elp_fraction * 10.0 ** -(digits - 1).""" + max_digits_int64 = int(math.ceil(math.log10(2**64 - 1))) + 1 + for d in range(1, max_digits_int64 + 1): + error_fraction = elp_fraction * (10.0 ** -(d - 1)) + # use ceil rather than round, to work around floating-point inaccuracy + e = int(math.ceil(n * error_fraction)) + assert(round_sig_dig(n, digits=d) >= n - e) + assert(round_sig_dig(n, digits=d) <= n + e) + + +def test_round_sig_dig(): + """Test rounding to a number of significant digits.""" + # Expected values + assert(round_sig_dig(11, 1) == 10) + assert(round_sig_dig(11, 2) == 11) + + assert(round_sig_dig(15, 1) == 20) + assert(round_sig_dig(15, 2) == 15) + + assert(round_sig_dig(54, 1) == 50) + assert(round_sig_dig(54, 2) == 54) + + assert(round_sig_dig(96, 1) == 100) + assert(round_sig_dig(96, 2) == 96) + + assert(round_sig_dig(839, 1) == 800) + assert(round_sig_dig(839, 2) == 840) + assert(round_sig_dig(839, 3) == 839) + + assert(round_sig_dig(5789, 1) == 6000) + assert(round_sig_dig(5789, 2) == 5800) + assert(round_sig_dig(5789, 3) == 5790) + assert(round_sig_dig(5789, 4) == 5789) + + assert(round_sig_dig(24103, 1) == 20000) + assert(round_sig_dig(24103, 2) == 24000) + assert(round_sig_dig(24103, 3) == 24100) + assert(round_sig_dig(24103, 4) == 24100) + assert(round_sig_dig(24103, 5) == 24103) + + assert(round_sig_dig(300000, 1) == 300000) + + # Floating-point values + + # Must round based on fractions, must not double-round + assert(round_sig_dig(14, 1) == 10) + assert(round_sig_dig(14.0, 1) == 10) + assert(round_sig_dig(14.9, 1) == 10) + assert(round_sig_dig(15.0, 1) == 20) + assert(round_sig_dig(15.1, 1) == 20) + + assert(round_sig_dig(14, 2) == 14) + assert(round_sig_dig(14.0, 2) == 14) + assert(round_sig_dig(14.9, 2) == 15) + assert(round_sig_dig(15.0, 2) == 15) + assert(round_sig_dig(15.1, 2) == 15) + + # Must round to integer + assert(round_sig_dig(14, 3) == 14) + assert(round_sig_dig(14.0, 3) == 14) + assert(round_sig_dig(14.9, 3) == 15) + assert(round_sig_dig(15.0, 3) == 15) + assert(round_sig_dig(15.1, 3) == 15) + + # Small integers + assert_round_sig_dig_any_digits(0, 1) + assert_round_sig_dig_any_digits(1, 1) + assert_round_sig_dig_any_digits(2, 2) + assert_round_sig_dig_any_digits(3, 3) + assert_round_sig_dig_any_digits(4, 4) + assert_round_sig_dig_any_digits(5, 5) + assert_round_sig_dig_any_digits(6, 6) + assert_round_sig_dig_any_digits(7, 7) + assert_round_sig_dig_any_digits(8, 8) + assert_round_sig_dig_any_digits(9, 9) + assert_round_sig_dig_any_digits(10, 10) + + # Large values + assert_round_sig_dig_any_digits_error(2**30) + assert_round_sig_dig_any_digits_error(2**31) + assert_round_sig_dig_any_digits_error(2**32) + + # the floating-point accuracy limit for this function is 2**73 + # on some machines + assert_round_sig_dig_any_digits_error(2**62) + assert_round_sig_dig_any_digits_error(2**63) + assert_round_sig_dig_any_digits_error(2**64) + + # Out of range values: must round to 1 + assert_round_sig_dig_any_digits(-0.01, 1) + assert_round_sig_dig_any_digits(-1, 1) + assert_round_sig_dig_any_digits(-10.5, 1) + assert_round_sig_dig_any_digits(-(2**31), 1) + + # test the transition points in the supported range + # testing the entire range up to 1 million takes 100s + for n in range(1, 20000): + assert_round_sig_dig_any_digits_error(n) + + # use a step that is relatively prime, to increase the chance of + # detecting errors + for n in range(90000, 200000, 9): + assert_round_sig_dig_any_digits_error(n) + + for n in range(900000, 2000000, 99): + assert_round_sig_dig_any_digits_error(n) + + def test_v3bwline_from_results_file(datadir): lines = datadir.readlines('results.txt') d = dict() @@ -99,10 +246,10 @@ if fp not in d: d[fp] = [] d[fp].append(r) - bwl = V3BWLine.from_data(d, fp) + bwl, _ = V3BWLine.from_data(d, fp) # bw store now B, not KB bwl.bw = round(bwl.bw / 1000) - assert bwl_str == str(bwl) + assert raw_bwl_str == str(bwl) def test_from_results_read(datadir, tmpdir, conf, args): @@ -110,9 +257,18 @@ expected_header = V3BWHeader(timestamp_l, earliest_bandwidth=earliest_bandwidth, latest_bandwidth=latest_bandwidth) - expected_bwls = [V3BWLine.from_results(results[fp]) for fp in results] - # bw store now B, not KB - expected_bwls[0].bw = round(expected_bwls[0].bw / 1000) + exclusion_dict = dict( + [(k, 0) for k in BW_HEADER_KEYVALUES_RECENT_MEASUREMENTS_EXCLUDED] + ) + expected_header.add_relays_excluded_counters(exclusion_dict) + raw_bwls = [V3BWLine.from_results(results[fp])[0] for fp in results] + # Scale BWLines using torflow method, since it's the default and BWLines + # bandwidth is the raw bandwidth. + expected_bwls = V3BWFile.bw_torflow_scale(raw_bwls) + # Since the scaled lines will be less than the 60% relays in the network, + # set under_min_report. + expected_bwls[0].under_min_report = 1 + expected_bwls[0].vote = 0 expected_f = V3BWFile(expected_header, expected_bwls) # This way is going to convert bw to KB v3bwfile = V3BWFile.from_results(results) @@ -145,16 +301,45 @@ assert v3bwfile.bw_lines[0].bw == 8 -def test_torflow_scale(datadir): +def num_consensus_relays(fpath): + return 1 + + +# To do not have to create a consensus-cache file and set the path, +# mock the result since it only returns the number of relays. +@mock.patch.object(V3BWFile, 'read_number_consensus_relays') +def test_torflow_scale(mock_consensus, datadir, tmpdir, conf): + mock_consensus.return_value = 1 + # state_fpath = str(tmpdir.join('.sbws', 'state.dat')) + state_fpath = conf['paths']['state_fpath'] results = load_result_file(str(datadir.join("results.txt"))) - v3bwfile = V3BWFile.from_results(results, scaling_method=TORFLOW_SCALING) - assert v3bwfile.bw_lines[0].bw == 1000 - v3bwfile = V3BWFile.from_results(results, scaling_method=TORFLOW_SCALING, - torflow_cap=0.0001) - assert v3bwfile.bw_lines[0].bw == 1000 - v3bwfile = V3BWFile.from_results(results, scaling_method=TORFLOW_SCALING, - torflow_cap=1, torflow_round_digs=0) - assert v3bwfile.bw_lines[0].bw == 524 + # Since v1.1.0, it'll write bw=1 if the minimum percent of measured relays + # wasn't reached. Therefore mock the consensus number. + # Because the consensus number is mocked, it'll try to read the sate path. + # Obtain it from conf, so that the root directory exists. + v3bwfile = V3BWFile.from_results(results, '', '', + state_fpath, + scaling_method=TORFLOW_SCALING, + round_digs=TORFLOW_ROUND_DIG) + assert v3bwfile.bw_lines[0].bw == 123 + v3bwfile = V3BWFile.from_results(results, '', '', + state_fpath, + scaling_method=TORFLOW_SCALING, + torflow_cap=0.0001, + round_digs=TORFLOW_ROUND_DIG) + assert v3bwfile.bw_lines[0].bw == 123 + v3bwfile = V3BWFile.from_results(results, '', '', + state_fpath, + scaling_method=TORFLOW_SCALING, + torflow_cap=1, + round_digs=TORFLOW_ROUND_DIG) + assert v3bwfile.bw_lines[0].bw == 123 + v3bwfile = V3BWFile.from_results(results, '', '', + state_fpath, + scaling_method=TORFLOW_SCALING, + torflow_cap=1, + round_digs=PROP276_ROUND_DIG) + assert v3bwfile.bw_lines[0].bw == 120 def test_results_away_each_other(datadir): @@ -163,24 +348,52 @@ results = load_result_file(str(datadir.join("results_away.txt"))) # A has 4 results, 3 are success, 2 are 1 day away, 1 is 12h away values = results["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"] + + # There is one result excluded, but the relay is not excluded + bwl, reason = V3BWLine.from_results(values, secs_away=secs_away, min_num=2) + assert bwl.relay_recent_measurements_excluded_error_count == 1 + assert reason is None + assert not hasattr(bwl, "vote") + assert not hasattr(bwl, "unmeasured") + success_results = [r for r in values if isinstance(r, ResultSuccess)] assert len(success_results) >= min_num results_away = V3BWLine.results_away_each_other(success_results, secs_away) assert len(results_away) == 3 + # B has 2 results, 12h away from each other values = results["BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"] + + # Two measurements are excluded and there were only 2, + # the relay is excluded + bwl, reason = V3BWLine.from_results(values, secs_away=secs_away, min_num=2) + assert bwl.relay_recent_measurements_excluded_near_count == 2 + assert reason == 'recent_measurements_excluded_near_count' + assert bwl.vote == 0 + assert bwl.unmeasured == 1 + success_results = [r for r in values if isinstance(r, ResultSuccess)] assert len(success_results) >= min_num results_away = V3BWLine.results_away_each_other(success_results, secs_away) - assert results_away is None + assert not results_away + secs_away = 43200 # 12h values = results["BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"] success_results = [r for r in values if isinstance(r, ResultSuccess)] assert len(success_results) >= min_num results_away = V3BWLine.results_away_each_other(success_results, secs_away) assert len(results_away) == 2 + # C has 1 result values = results["CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"] + + # There is only 1 result, the relay is excluded + bwl, reason = V3BWLine.from_results(values, min_num=2) + assert bwl.relay_recent_measurements_excluded_few_count == 1 + assert reason == 'recent_measurements_excluded_few_count' + assert bwl.vote == 0 + assert bwl.unmeasured == 1 + success_results = [r for r in values if isinstance(r, ResultSuccess)] assert len(success_results) < min_num @@ -197,19 +410,19 @@ results = load_result_file(str(datadir.join("results_away.txt"))) for fp, values in results.items(): # log.debug("Relay fp %s", fp) - line = V3BWLine.from_results(values) + line, _ = V3BWLine.from_results(values) if line is not None: bw_lines_raw.append(line) assert len(bw_lines_raw) == 3 bw_lines = V3BWFile.bw_torflow_scale(bw_lines_raw) assert len(bw_lines) == 3 statsd, success = V3BWFile.measured_progress_stats( - bw_lines, number_consensus_relays, min_perc_reached_before) + len(bw_lines), number_consensus_relays, min_perc_reached_before) assert success assert statsd == statsd_exp number_consensus_relays = 6 statsd, success = V3BWFile.measured_progress_stats( - bw_lines, number_consensus_relays, min_perc_reached_before) + len(bw_lines), number_consensus_relays, min_perc_reached_before) assert not success statsd_exp = {'percent_eligible_relays': 50, 'minimum_percent_eligible_relays': 60, @@ -231,7 +444,7 @@ if line is not None: bw_lines_raw.append(line) bwfile = V3BWFile(header, []) - bwfile.update_progress(bw_lines_raw, header, number_consensus_relays, + bwfile.update_progress(len(bw_lines_raw), header, number_consensus_relays, state) assert header.percent_eligible_relays == '50' assert state.get('min_perc_reached') is None @@ -239,7 +452,7 @@ # relays number_consensus_relays = 3 header = V3BWHeader(str(now_unixts())) - bwfile.update_progress(bw_lines_raw, header, number_consensus_relays, + bwfile.update_progress(len(bw_lines_raw), header, number_consensus_relays, state) assert state.get('min_perc_reached') == now_isodt_str() assert header.minimum_number_eligible_relays == '2' @@ -247,3 +460,52 @@ assert header.number_consensus_relays == '3' assert header.number_eligible_relays == '3' assert header.percent_eligible_relays == '100' + + +def test_time_measure_half_network(caplog): + header = V3BWHeader(timestamp_l, + file_created=file_created, + generator_started=generator_started, + earliest_bandwidth=earliest_bandwidth) + header.number_consensus_relays = '6500' + header.number_eligible_relays = '4000' + caplog.set_level(logging.INFO) + header.add_time_report_half_network() + assert header.time_to_report_half_network == '70200' # 19.5h + expected_log = "Estimated time to measure the network: 39 hours." # 19.5*2 + assert caplog.records[-1].getMessage() == expected_log + + +@mock.patch.object(V3BWFile, 'read_number_consensus_relays') +def test_set_under_min_report(mock_consensus, conf, datadir): + # The number of relays (1) is the same as the ones in the consensus, + # therefore there is no any relay excluded and under_min_report is not set. + mock_consensus.return_value = 1 + state_fpath = conf['paths']['state_fpath'] + results = load_result_file(str(datadir.join("results.txt"))) + v3bwfile = V3BWFile.from_results(results, '', '', state_fpath) + bwl = v3bwfile.bw_lines[0] + assert not hasattr(bwl, "vote") + assert not hasattr(bwl, "under_min_report") + assert bwl.bw != 1 + + # The number of relays is the same as the ones in the consensus, + # but after filtering there's no any, under_min_report is set to 1 + # and unmeasured was also set to 1. + # After filtering the relay is excluded because there's only 1 success + # result and it should have at least 2 (min_num) + v3bwfile = V3BWFile.from_results(results, '', '', state_fpath, min_num=2) + bwl = v3bwfile.bw_lines[0] + assert bwl.vote == 0 + assert bwl.under_min_report == 1 + assert bwl.unmeasured == 1 + assert bwl.bw == 1 + + # The number of relays after scaling is than the 60% in the network, + # therefore the relays are excluded and under_min_report is set to 1. + mock_consensus.return_value = 3 + v3bwfile = V3BWFile.from_results(results, '', '', state_fpath) + bwl = v3bwfile.bw_lines[0] + assert bwl.vote == 0 + assert bwl.under_min_report == 1 + assert bwl.bw != 1 diff -Nru sbws-1.0.2/tests/unit/util/data/user_sbws.ini sbws-1.1.0/tests/unit/util/data/user_sbws.ini --- sbws-1.0.2/tests/unit/util/data/user_sbws.ini 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/unit/util/data/user_sbws.ini 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,3 @@ +[paths] + +sbws_home = /tmp/.sbws \ No newline at end of file diff -Nru sbws-1.0.2/tests/unit/util/test_config.py sbws-1.1.0/tests/unit/util/test_config.py --- sbws-1.0.2/tests/unit/util/test_config.py 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tests/unit/util/test_config.py 2019-03-29 13:47:26.000000000 +0000 @@ -190,14 +190,17 @@ def test_validate_url(): goods = [ - 'http://example.com', 'http://example.com/', - 'http://example.com/foo.bar', - 'http://example.com/foo/bar', - 'http://user@example.com', + 'https://example.com', 'https://example.com/', + 'https://example.com/foo.bar', + 'https://example.com/foo/bar', + 'https://user@example.com', + 'https://48.290.983.123:4443', + 'http://127.0.0.1:8000' ] bads = [ 'ftp://example.com/foo.bar', 'http://', 'http:///', + 'http://example.com', ] for val in goods: d = {'': val} @@ -230,3 +233,46 @@ d = {'n': nick} valid, reason = con._validate_nickname(d, 'n') assert not valid, reason + + +def test_country(conf): + from string import Template + err_tmpl = Template('$sec/$key ($val): $e') + + # Invalid default country code in scanner section + errors = con._validate_country(conf, 'scanner', 'country', err_tmpl) + assert errors[0] == \ + 'scanner/country (AA): Not a valid ISO 3166 alpha-2 country code.' + + # Valid country code in scanner section + conf['scanner']['country'] = 'US' + errors = con._validate_country(conf, 'scanner', 'country', err_tmpl) + assert not errors + + # No country in destinations.foo section + conf['destinations']['foo'] = 'on' + conf['destinations.foo'] = {} + conf['destinations.foo']['url'] = 'https://foo.bar' + errors = con._validate_country( + conf, 'destinations.foo', 'country', err_tmpl) + assert errors[0] == \ + 'destinations.foo/country (None): ' \ + 'Missing country in configuration file.' + + # Valid country in destinations.foo section + conf['destinations.foo']['url'] = 'US' + errors = con._validate_country(conf, 'scanner', 'country', err_tmpl) + assert not errors + + +def test_config_arg_provided_but_no_found(args, conf): + args.config = 'non_existing_file' + user_conf = con._get_user_config(args, conf) + # since the user configuration is not found, it is the same as conf + assert conf.__dict__.items() == user_conf.__dict__.items() + + +def test_config_arg_provided(args, conf, datadir): + args.config = datadir.join('user_sbws.ini') + user_conf = con._get_user_config(args, conf) + assert user_conf['paths']['sbws_home'] == '/tmp/.sbws' diff -Nru sbws-1.0.2/tests/unit/util/test_stem.py sbws-1.1.0/tests/unit/util/test_stem.py --- sbws-1.0.2/tests/unit/util/test_stem.py 1970-01-01 00:00:00.000000000 +0000 +++ sbws-1.1.0/tests/unit/util/test_stem.py 2019-03-29 13:47:26.000000000 +0000 @@ -0,0 +1,34 @@ +"""Unit tests for stem.py""" + +from sbws.util.stem import parse_user_torrc_config + + +def test_parse_user_torrc_config_new_keyvalue_options_success(): + config_torrc_extra_lines = """ + Log debug file /tmp/tor-debug.log + NumCPUs 1 + """ + torrc_dict = parse_user_torrc_config({}, config_torrc_extra_lines) + assert torrc_dict == \ + {'Log': 'debug file /tmp/tor-debug.log', 'NumCPUs': '1'} + + +def test_parse_user_torrc_config_existing_keyvalue_options_fail(caplog): + torrc_dict = {'SocksPort': 'auto'} + config_torrc_extra_lines = """ + SocksPort 9050 + """ + torrc_dict_new = parse_user_torrc_config( + torrc_dict, config_torrc_extra_lines) + # the new dictionary contains the existing key option and a list with both + # the existing value and the new value + assert torrc_dict_new != torrc_dict + assert torrc_dict_new == {'SocksPort': ['auto', '9050']} + + +def test_parse_user_torrc_config_new_key_option_success(): + config_torrc_extra_lines = """ + LongLivedPorts + """ + torrc_dict = parse_user_torrc_config({}, config_torrc_extra_lines) + assert torrc_dict == {'LongLivedPorts': None} diff -Nru sbws-1.0.2/tox.ini sbws-1.1.0/tox.ini --- sbws-1.0.2/tox.ini 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/tox.ini 2019-03-29 13:47:26.000000000 +0000 @@ -1,6 +1,6 @@ [tox] skip_missing_interpreters = True -envlist = py35, py36, inst, setup, unit, integration, lint, stats, doc, clean +envlist = py35, py36, inst, setup, unit, integration, lint, stats, doc [travis] python = @@ -39,19 +39,29 @@ ignore_errors = True deps = .[test] whitelist_externals = - tar + cp bash sleep wget + mkdir + rm commands = - tar -C {envtmpdir} -vxf {toxinidir}/tests/integration/net.tar + cp -af {toxinidir}/tests/integration/net {envtmpdir} bash {envtmpdir}/net/start.sh bash -c "time python3 {envtmpdir}/net/wait.py {envtmpdir}/net/{auth,relay,exit}*" bash -c "python3 {toxinidir}/scripts/tools/sbws-http-server.py --port 28888 &>/dev/null &" - sleep 15 + sleep 1 wget -O/dev/null http://127.0.0.1:28888/sbws.bin + ; Run actually the scanner + mkdir -p /tmp/.sbws + ; This add around 3min more to the tests + sbws -c {toxinidir}/tests/integration/sbws_testnet.ini scanner + sbws -c {toxinidir}/tests/integration/sbws_testnet.ini generate coverage run -a --rcfile={toxinidir}/.coveragerc --source=sbws -m pytest -s {toxinidir}/tests/integration -vv + sbws -c {toxinidir}/tests/integration/sbws_testnet.ini cleanup bash {envtmpdir}/net/stop.sh + # no need to remove .tox/net directory. + rm -rf /tmp/.sbws [testenv:lint] skip_install = True @@ -82,5 +92,12 @@ make html # this requires build the pdf images # make latexpdf - # this requires network - # make linkcheck + make man + +# this requires Internet, it should not be in envlist +[testenv:doclinks] +deps = .[doc] +whitelist_externals = make +changedir = docs +commands = + make linkcheck diff -Nru sbws-1.0.2/.travis.yml sbws-1.1.0/.travis.yml --- sbws-1.0.2/.travis.yml 2018-11-10 15:00:29.000000000 +0000 +++ sbws-1.1.0/.travis.yml 2019-03-29 13:47:26.000000000 +0000 @@ -1,17 +1,21 @@ sudo: true +dist: xenial language: python python: - "3.5" - "3.6" install: pip install tox-travis -script: tox +script: + - tox + # This is not in included in the tox envlist, in order to don't need Internet + # when running tox + # - tox -e doclinks + - tox -e clean before_install: - gpg --version - - gpg --keyserver hkp://pool.sks-keyservers.net --recv A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 - - gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | sudo apt-key add - - echo "deb https://deb.torproject.org/torproject.org trusty main" | sudo tee -a /etc/apt/sources.list - - echo "deb-src https://deb.torproject.org/torproject.org trusty main" | sudo tee -a /etc/apt/sources.list + - sudo apt-key add tests/deb.torproject.org.asc + - echo "deb https://deb.torproject.org/torproject.org xenial main" | sudo tee -a /etc/apt/sources.list - sudo apt-get update -qq - sudo apt-get install tor -y @@ -22,3 +26,10 @@ template: - "%{repository_slug} %{branch} %{commit} - %{author}: %{commit_subject}" - "Build #%{build_number} %{result}. Details: %{build_url}" + +# To build the docs +addons: + apt: + packages: + - texlive-latex-extra + - dvipng