Winnow

How Winnow is tested

Start with what a person needs to do. Check the screens, the wallet’s decisions, and what the Bitcoin network accepts. Keep enough evidence to investigate a failure.

Browse app journeys & node results · Browse package & app checks

One payment, several ways to check it

  1. 1. Use the appEnter a recipient, review the amount and fee, and confirm the payment.
  2. 2. Check the decisionsVerify selected coins, signatures, saved state, and recovery when something interrupts the flow.
  3. 3. Ask Bitcoin CoreHave an independent node accept the transaction and include it in a block.
  4. 4. Read the resultCheck the app’s history, assertions, and recorded evidence for that exact revision.

These checks examine different parts of the same promise. A feature earns its place through a documented journey; focused tests also protect the security rules and failure cases that a successful screen flow cannot establish. Read the testing and feature policy.

The test suites

Real app journeys

The tests operate the iPhone app in a simulator: create and recover a wallet, receive and send, pay a person, coordinate shared savings, and use advanced signing or fee replacement.

A disposable Bitcoin test network supplies real blocks and transactions. Assertions check what appears on screen and what the node sees. Screenshots capture selected moments along the way.

App state & protected actions

Smaller app tests isolate decisions that are difficult to exhaust through taps: changing a recipient after review, reopening saved state, separating networks, handling damaged files, and requiring device authentication.

They also check how wallet state becomes something understandable on screen, such as keeping a replaced payment beside its replacement.

Wallet & network rules

These checks exercise balances, fees, coin selection, backups, transaction relay, block headers, and compact filters. Controlled peers and damaged inputs test disagreement, disconnects, reorganization, and recovery.

Wallet and networking live together in WalletCore. Focused tests make failures in their shared rules easier to reproduce.

Cryptography & data formats

Known-answer examples, including published Bitcoin test vectors, check keys, signatures, descriptors, and encodings. Invalid inputs and boundary cases check that malformed data is rejected.

A known-answer test compares with an expected result established outside the code being tested. It helps catch two pieces of our own code agreeing on the same mistake.

Comparison with Bitcoin Core

“Differential” tests ask another implementation to judge our work. Bitcoin Core checks transactions, replacement fees, signing envelopes, and co-signing, while our wallet discovers and confirms payments through its peer protocol.

The fixture starts with its own fresh test chain. Passing shows agreement for the exercised cases and Core version.

Fuzzing & sanitizer runs

Fuzzing feeds many generated inputs into nine parsers, including transactions, network messages, and wallet imports. A fixed input sequence runs in routine CI; longer weekly runs rotate the sequence to explore further.

Address Sanitizer looks for memory errors. Thread Sanitizer looks for data races. A saved seed identifies the input sequence so a finding can be replayed and kept as a regression test.

Debugging tools & live-network runs

Tool tests check command behavior and known fuzz regressions. Operators can also collect simulator logs or run a longer “soak” against public signet to observe peer connections, sync progress, and resource use over time.

These live observations depend on the network and machine used. A command’s help check does not exercise a live network run.

Release & website checks

Build checks verify dependency resolution, compiler warnings, and exclusion of test-only controls from the release app. Complexity and size reports make growth visible.

Website checks verify local links and images, and connect public feature descriptions to real app scenarios. They catch documentation drift; the scenario’s result still comes from its test run.

When do these checks run?

Routine CI runs package and app checks, release-build checks, and the fixed fuzz corpus for pull requests and main. The full node and app-journey suites run for relevant code changes from this repository, on main, and on their nightly or explicitly dispatched runs. Fork pull requests do not use the dedicated node runner. Documentation-only changes can skip those node jobs.

The longer sanitizer workflow runs weekly or on request. Live-network soaks and device exercises are separate observations. A release invokes CI and node checks again. Read the node workflow, fuzz schedule, and release workflow for the exact conditions.

How to read the evidence

An artifact is a file or bundle saved by a run: its detailed results, logs, screenshots, or inputs needed to reproduce a problem.

  1. Choose a run and check its revision. Confirm the commit, branch, date, and which jobs actually ran. A skipped job supplies no new test result.
  2. Read the conclusion and failures. Open the relevant job and its test step. A run that stopped early may still upload partial evidence.
  3. Open Artifacts on the run’s summary. Download the matching bundle and use the guide below. GitHub requires sign-in and repository read access for downloads; see its artifact download guide.
The names below come from the current workflows. The run number is appended to most download names.
EvidenceWhat it tells youHow to use it
Job logs
Package, app, node, and build steps
Which tests executed, their failures, and the context around a build or runtime error.Read directly on GitHub Actions. Start with the failing test or step; test counts alone do not explain coverage.
App result bundles
node-ui-…
app-tests-…
NodeUI.xcresult and AppTests.xcresult hold per-test results and attachments. The bundles also include readable test logs.Unzip, then open the .xcresult in Xcode to inspect a test and its failure details. Without Xcode, start with node-ui.log or app-tests.log. Retained for 14 days.
Screenshots & timings
Inside node-ui-…
Selected screen states and measured steps from that run. timings.json contains the current process’s observations; incomplete runs can contain fewer scenarios.View the PNGs and read the JSON as text. Pair them with the test result and revision. A capture shows a moment, and a simulator timing is specific to that machine. Retained with the UI bundle.
Core comparison log
node-differential-…
differential.log records the independent-node checks, including acceptance, co-signing, and confirmation outcomes.Open as text and find the named scenario and its conclusion. Use the node workflow runs to choose a revision. Retained for 14 days.
Fuzz logs & reproducers
fuzz-smoke-…
fuzz-address-…
fuzz-thread-…
run.log describes the run. Longer runs save seed.txt. An interrupted case can leave in-flight.txt; detected invariant failures save input bytes.Keep the revision, target selection, seed, input limit, and iteration count together. Follow the replay guide. Smoke artifacts last 7 days; longer-run artifacts last 14 days.
Build provenance & dependencies
production-supply-chain-…
winnow.spdx.json lists build components. winnow-build-provenance.json records the source and build inputs tied to the output.Read the JSON alongside the build log. These describe what was built; they are not an independent security review. CI artifacts last 14 days; release metadata is also attached to GitHub releases.
Size reports
loc-…
loc.md, loc.csv, and loc.json show counted code and changes against the comparison revision.Read the workflow summary first, then the files for detail. The LOC workflow retains these artifacts for 90 days.
Soak & device observationsJSONL files record one observation per line over a live-network run. Dated screenshots and notes document specific device or manual exercises.Check the date, device, network, and source revision. The August audit record links its scope and evidence; the August 24 soak file is a historical example.

Downloads expire and runs can be removed. If an example’s bundle is no longer available, choose a newer completed run in the same workflow and check its revision. Historical evidence remains evidence for its recorded conditions.

A real example: finding a payment after a fee bump

7 September 2026. The new fee-replacement journey found that Bitcoin Core accepted the replacement, but the app buried the original payment below older confirmed receipts. Scrolling a little further did not solve the experience.

The fix keeps the replacement chain together in history and moves it with the final payment when it confirms. The app journey checks the exact original and replacement; focused tests cover repeated bumps, confirmation, and damaged links.

The failing run · The change and explanation · Focused regression tests

The passing example below tested revision fe865f2. It is a dated worked example, not a live status display.

Those UI, node, and app bundles are scheduled to expire on 21 September 2026. For newer evidence, use the workflow links above.

What remains to prove

A simulator cannot establish locked-device Keychain enforcement, battery use on an iPhone, or independence of peers on the public network. Those require their own device and field evidence. Independent security review has a separately stated scope and revision.

We keep meaningful cryptographic, hostile-input, storage, authorization, and recovery checks even when a successful app journey cannot reach their failure cases. Obsolete features and duplicate paths can leave with their tests.

Read the separate roadmap · Testing and feature policy · Detailed suite map and historical scope