UNPKG

@aladas-org/cryptocalc

Version:
815 lines (775 loc) 33.5 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>E2E Test Protocols — HD Wallet</title> <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&family=Syne:wght@400;600;700;800&display=swap" rel="stylesheet" /> <style> *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } :root { --bg: #0b0d12; --surface: #111420; --border: #1e2235; --border-hi: #2e3450; --text: #cdd3e8; --muted: #5a6080; --accent: #f7c948; --accent2: #3d8ef7; --accent3: #50e0a0; --accent4: #e05080; --pass: #50e0a0; --fail: #e05080; --mono: 'JetBrains Mono', monospace; --sans: 'Syne', sans-serif; } body { background: var(--bg); color: var(--text); font-family: var(--sans); font-size: 15px; line-height: 1.7; padding: 0 0 80px; } /* ── HEADER ── */ header { padding: 56px 64px 40px; border-bottom: 1px solid var(--border); position: relative; overflow: hidden; } header::before { content: ''; position: absolute; inset: 0; background: radial-gradient(ellipse 60% 80% at 10% 50%, #1a2040 0%, transparent 70%), radial-gradient(ellipse 40% 60% at 90% 20%, #0d1a30 0%, transparent 70%); z-index: 0; } header > * { position: relative; z-index: 1; } .header-label { font-family: var(--mono); font-size: 11px; letter-spacing: .18em; color: var(--accent); text-transform: uppercase; margin-bottom: 16px; } h1 { font-size: clamp(26px, 4vw, 40px); font-weight: 800; color: #eef0f8; line-height: 1.15; margin-bottom: 24px; } h1 span { color: var(--accent); } .meta { display: flex; flex-wrap: wrap; gap: 10px; } .meta-item { font-family: var(--mono); font-size: 12px; background: var(--surface); border: 1px solid var(--border-hi); border-radius: 4px; padding: 5px 12px; color: var(--muted); } .meta-item strong { color: var(--text); } /* ── LAYOUT ── */ main { max-width: 1060px; margin: 0 auto; padding: 48px 64px 0; } /* ── SECTION TITLES ── */ .section-title { font-family: var(--mono); font-size: 10px; letter-spacing: .2em; color: var(--accent); text-transform: uppercase; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 1px solid var(--border); } /* ── CONTEXT BOX ── */ .context-block { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 28px 32px; margin-bottom: 48px; } .context-block p { color: var(--text); margin-bottom: 16px; } .context-block p:last-child { margin-bottom: 0; } /* ── CONSTRAINTS TABLE ── */ .constraint-grid { display: grid; grid-template-columns: 1fr; gap: 10px; margin: 20px 0; } .constraint-row { display: grid; grid-template-columns: 1fr 1.6fr; gap: 0; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; font-size: 13px; } .constraint-key { background: #141828; padding: 10px 16px; font-family: var(--mono); color: var(--accent2); border-right: 1px solid var(--border); } .constraint-val { background: var(--surface); padding: 10px 16px; color: var(--muted); } /* ── HELPERS BLOCK ── */ .helpers-list { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; } .helper-row { display: flex; align-items: baseline; gap: 16px; font-size: 13px; } .helper-name { font-family: var(--mono); color: var(--accent3); min-width: 280px; flex-shrink: 0; } .helper-desc { color: var(--muted); } /* ── TEST CARDS ── */ .tests-grid { display: flex; flex-direction: column; gap: 32px; margin-bottom: 48px; } .test-card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; } .test-card-header { display: flex; align-items: center; gap: 20px; padding: 20px 28px; border-bottom: 1px solid var(--border); background: #0f1220; } .test-number { width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-family: var(--mono); font-size: 13px; font-weight: 700; flex-shrink: 0; } .test-card:nth-child(1) .test-number { background: #1a2a3a; color: var(--accent2); border: 1px solid #2a4060; } .test-card:nth-child(2) .test-number { background: #1a2a2a; color: var(--accent3); border: 1px solid #204040; } .test-card:nth-child(3) .test-number { background: #2a2010; color: var(--accent); border: 1px solid #403010; } .test-card:nth-child(4) .test-number { background: #1a1040; color: #a080ff; border: 1px solid #302070; } .test-card:nth-child(5) .test-number { background: #2a1018; color: var(--accent4); border: 1px solid #401020; } .test-title-wrap { flex: 1; } .test-id { font-family: var(--mono); font-size: 10px; color: var(--muted); letter-spacing: .12em; text-transform: uppercase; margin-bottom: 4px; } .test-title { font-size: 15px; font-weight: 700; color: #eef0f8; } .test-title code { font-family: var(--mono); font-size: 13px; color: var(--accent); background: #1a1c28; padding: 1px 6px; border-radius: 3px; } .test-body { padding: 24px 28px; display: flex; flex-direction: column; gap: 24px; } /* sub-sections */ .sub-label { font-family: var(--mono); font-size: 10px; letter-spacing: .15em; color: var(--muted); text-transform: uppercase; margin-bottom: 10px; } .objective-text { color: var(--text); font-size: 14px; line-height: 1.65; } /* preconditions */ .precond-list { display: flex; flex-wrap: wrap; gap: 8px; } .precond-chip { font-family: var(--mono); font-size: 12px; padding: 4px 12px; border-radius: 20px; background: #141828; border: 1px solid var(--border-hi); color: var(--text); } /* steps table */ .steps-table { width: 100%; border-collapse: collapse; font-size: 13px; } .steps-table th { font-family: var(--mono); font-size: 10px; letter-spacing: .12em; color: var(--muted); text-transform: uppercase; text-align: left; padding: 8px 14px; background: #0f1220; border-bottom: 1px solid var(--border); } .steps-table td { padding: 9px 14px; border-bottom: 1px solid var(--border); vertical-align: top; } .steps-table tr:last-child td { border-bottom: none; } .steps-table tr:hover td { background: #141828; } .step-num { font-family: var(--mono); color: var(--muted); width: 30px; } .step-action { color: var(--text); } .step-method { font-family: var(--mono); font-size: 12px; color: var(--accent3); } /* assertion box */ .assertion-box { background: #0f1220; border: 1px solid var(--border-hi); border-left: 3px solid var(--accent3); border-radius: 4px; padding: 14px 18px; font-family: var(--mono); font-size: 13px; color: var(--accent3); white-space: pre; } .assertion-box.ineq { border-left-color: var(--accent2); color: var(--accent2); } .assertion-box.eq { border-left-color: var(--accent4); color: var(--accent4); } /* failure list */ .failure-list { list-style: none; display: flex; flex-direction: column; gap: 8px; } .failure-list li { display: flex; gap: 12px; font-size: 13px; align-items: baseline; } .fail-bullet { font-family: var(--mono); color: var(--fail); flex-shrink: 0; } .fail-text { color: var(--muted); } .fail-text strong { color: var(--text); font-weight: 600; } /* design note */ .design-note { background: #130d20; border: 1px solid #2a1a40; border-left: 3px solid #a080ff; border-radius: 4px; padding: 14px 18px; font-size: 13px; color: #9070cc; line-height: 1.65; } .design-note strong { color: #c0a0ff; } .design-note code { font-family: var(--mono); font-size: 12px; background: #1a1030; padding: 1px 5px; border-radius: 3px; color: var(--accent4); } /* ── SUMMARY TABLE ── */ .summary-table { width: 100%; border-collapse: collapse; font-size: 13px; } .summary-table th { font-family: var(--mono); font-size: 10px; letter-spacing: .12em; color: var(--muted); text-transform: uppercase; text-align: left; padding: 10px 16px; background: #0f1220; border-bottom: 1px solid var(--border); } .summary-table td { padding: 11px 16px; border-bottom: 1px solid var(--border); color: var(--text); } .summary-table tr:last-child td { border-bottom: none; } .summary-table tr:hover td { background: #141828; } .badge { font-family: var(--mono); font-size: 11px; padding: 3px 10px; border-radius: 12px; display: inline-block; } .badge-regex { background: #0f2030; color: var(--accent2); border: 1px solid #1a4060; } .badge-ineq { background: #101a10; color: var(--accent3); border: 1px solid #1a3020; } .badge-eq { background: #1a0820; color: var(--accent4); border: 1px solid #300a30; } .badge-btc { background: #201808; color: var(--accent); border: 1px solid #403010; } /* ── CODE INLINE ── */ code { font-family: var(--mono); font-size: 12.5px; color: var(--accent4); background: #1a1028; padding: 1px 6px; border-radius: 3px; } /* ── ENTROPY CHIP ── */ .entropy-value { font-family: var(--mono); font-size: 12px; color: var(--accent); background: #1a1408; border: 1px solid #403010; padding: 8px 14px; border-radius: 4px; word-break: break-all; margin-top: 8px; display: block; } /* ── SCROLLBAR ── */ ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: var(--bg); } ::-webkit-scrollbar-thumb { background: var(--border-hi); border-radius: 3px; } </style> </head> <body> <header> <div class="header-label">Playwright · Electron E2E</div> <h1>E2E Test Protocols<br><span>HD Wallet Use Case</span></h1> <div class="meta"> <div class="meta-item"><strong>File</strong> &nbsp;tests/playwright/e2e/hd_wallet_usecase.test.js</div> <div class="meta-item"><strong>Suite</strong> &nbsp;Use Case - HD Wallet : account, address index, BIP39 passphrase</div> <div class="meta-item"><strong>Tests</strong> &nbsp;5</div> <div class="meta-item"><strong>Blockchain</strong> &nbsp;Bitcoin</div> </div> </header> <main> <!-- ── GENERAL CONTEXT ── --> <div class="section-title">General Context</div> <div class="context-block"> <p> These tests drive the Cryptocalc application as a real user would, through its Electron GUI. Each test launches a fresh instance of the application, performs UI interactions, and verifies the resulting state of the wallet fields. </p> <div class="sub-label">Key Technical Constraints</div> <div class="constraint-grid"> <div class="constraint-row"> <div class="constraint-key">#account_id, #address_index_id</div> <div class="constraint-val">Fields are <code>readonly</code> — set via <code>page.evaluate()</code> to bypass Playwright's refusal to fill readonly inputs</div> </div> <div class="constraint-row"> <div class="constraint-key">#bip32_passphrase_id</div> <div class="constraint-val">Also <code>readonly</code> — direct DOM injection with <code>input</code> + <code>change</code> events dispatched</div> </div> <div class="constraint-row"> <div class="constraint-key">#refresh_btn_id</div> <div class="constraint-val">May be <code>disabled</code> — clicked via <code>evaluate()</code> after removing the <code>disabled</code> attribute</div> </div> <div class="constraint-row"> <div class="constraint-key">Refresh regenerates entropy</div> <div class="constraint-val">Each click produces new random entropy — determinism tests inject directly into <code>#entropy_id</code> instead</div> </div> <div class="constraint-row"> <div class="constraint-key">BIP32 derivation is async</div> <div class="constraint-val">A <code>waitForTimeout(3000)</code> pause follows every derivation trigger</div> </div> </div> <div class="sub-label" style="margin-top:20px;">Shared Helpers</div> <div class="helpers-list"> <div class="helper-row"><span class="helper-name">switchToHDWallet(page)</span><span class="helper-desc">Navigate to Wallet tab, select "HD Wallet" mode</span></div> <div class="helper-row"><span class="helper-name">setFieldValue(page, id, val)</span><span class="helper-desc">Inject a value into any field (readonly-safe), dispatches input + change</span></div> <div class="helper-row"><span class="helper-name">setBip39Passphrase(page, val)</span><span class="helper-desc">Wraps setFieldValue for <code>#bip32_passphrase_id</code></span></div> <div class="helper-row"><span class="helper-name">clickRefresh(page)</span><span class="helper-desc">Trigger Refresh via evaluate() — generates new random entropy</span></div> <div class="helper-row"><span class="helper-name">deriveFromFixedEntropy(page, e)</span><span class="helper-desc">Inject entropy into <code>#entropy_id</code> and trigger derivation without Refresh</span></div> <div class="helper-row"><span class="helper-name">getDisplayedAddress(page)</span><span class="helper-desc">Read the current text content of <code>#address_id</code></span></div> </div> </div> <!-- ── TESTS ── --> <div class="section-title">Test Protocols</div> <div class="tests-grid"> <!-- TEST 1 --> <div class="test-card"> <div class="test-card-header"> <div class="test-number">01</div> <div class="test-title-wrap"> <div class="test-id">Format Validation</div> <div class="test-title">Bitcoin address generated with <code>account=2</code>, <code>index=5</code>, passphrase is valid</div> </div> </div> <div class="test-body"> <div> <div class="sub-label">Objective</div> <p class="objective-text"> Verify that the HD Wallet derivation produces a well-formed Bitcoin address when given a non-default account, address index, and a BIP39 passphrase. The address must match one of the three valid Bitcoin address formats: Legacy (P2PKH), P2SH, or Bech32 (SegWit). </p> </div> <div> <div class="sub-label">Preconditions</div> <div class="precond-list"> <span class="precond-chip">App launched &amp; ready</span> <span class="precond-chip">Wallet tab active</span> <span class="precond-chip">Mode: HD Wallet</span> <span class="precond-chip">Blockchain: Bitcoin</span> </div> </div> <div> <div class="sub-label">Steps</div> <table class="steps-table"> <thead><tr><th>#</th><th>Action</th><th>Method</th></tr></thead> <tbody> <tr><td class="step-num">1</td><td class="step-action">Set <code>account</code> to <code>2</code></td><td class="step-method">setFieldValue(page, 'account_id', '2')</td></tr> <tr><td class="step-num">2</td><td class="step-action">Set <code>address index</code> to <code>5</code></td><td class="step-method">setFieldValue(page, 'address_index_id', '5')</td></tr> <tr><td class="step-num">3</td><td class="step-action">Set BIP39 passphrase to <code>"my secret passphrase"</code></td><td class="step-method">setBip39Passphrase(page, '...')</td></tr> <tr><td class="step-num">4</td><td class="step-action">Click Refresh to trigger derivation</td><td class="step-method">clickRefresh(page)</td></tr> <tr><td class="step-num">5</td><td class="step-action">Read generated address</td><td class="step-method">getDisplayedAddress(page)</td></tr> </tbody> </table> </div> <div> <div class="sub-label">Expected Result</div> <div class="assertion-box">/^(1[a-zA-HJ-NP-Z0-9]{25,34} ← Legacy P2PKH |3[a-zA-HJ-NP-Z0-9]{25,34} ← P2SH |bc1[a-z0-9]{6,87})$/ ← Bech32 SegWit</div> </div> <div> <div class="sub-label">Failure Scenarios</div> <ul class="failure-list"> <li><span class="fail-bullet"></span><span class="fail-text"><strong>Empty address</strong> — derivation did not complete within the 3s timeout</span></li> <li><span class="fail-bullet"></span><span class="fail-text"><strong>Regex mismatch</strong> — wrong blockchain selected, or unexpected address format returned</span></li> </ul> </div> </div> </div> <!-- TEST 2 --> <div class="test-card"> <div class="test-card-header"> <div class="test-number">02</div> <div class="test-title-wrap"> <div class="test-id">Passphrase Effect</div> <div class="test-title">BIP39 passphrase modifies the derived address (same account/index)</div> </div> </div> <div class="test-body"> <div> <div class="sub-label">Objective</div> <p class="objective-text"> Verify that the BIP39 passphrase acts as a second factor in HD derivation. With all other parameters identical, a non-empty passphrase must produce a different address than an empty passphrase, demonstrating that the passphrase is actually used in the BIP39 seed computation. </p> </div> <div> <div class="sub-label">Preconditions</div> <div class="precond-list"> <span class="precond-chip">App launched &amp; ready</span> <span class="precond-chip">Wallet tab active</span> <span class="precond-chip">Mode: HD Wallet</span> <span class="precond-chip">Blockchain: Bitcoin</span> </div> </div> <div> <div class="sub-label">Steps</div> <table class="steps-table"> <thead><tr><th>#</th><th>Action</th><th>Method</th></tr></thead> <tbody> <tr><td class="step-num">1</td><td class="step-action">Set <code>account=2</code>, <code>address index=5</code></td><td class="step-method">setFieldValue(…) × 2</td></tr> <tr><td class="step-num">2</td><td class="step-action">Set passphrase to <code>""</code> (empty)</td><td class="step-method">setBip39Passphrase(page, '')</td></tr> <tr><td class="step-num">3</td><td class="step-action">Click Refresh → read <code>addressWithoutPassphrase</code></td><td class="step-method">clickRefresh + getDisplayedAddress</td></tr> <tr><td class="step-num">4</td><td class="step-action">Set passphrase to <code>"my secret passphrase"</code></td><td class="step-method">setBip39Passphrase(page, '...')</td></tr> <tr><td class="step-num">5</td><td class="step-action">Click Refresh → read <code>addressWithPassphrase</code></td><td class="step-method">clickRefresh + getDisplayedAddress</td></tr> </tbody> </table> </div> <div> <div class="sub-label">Expected Result</div> <div class="assertion-box ineq">addressWithPassphrase ≠ addressWithoutPassphrase</div> </div> <div> <div class="sub-label">Failure Scenarios</div> <ul class="failure-list"> <li><span class="fail-bullet"></span><span class="fail-text"><strong>Both addresses equal</strong> — passphrase is not being incorporated in the BIP39 seed computation</span></li> <li><span class="fail-bullet"></span><span class="fail-text"><strong>Note:</strong> absolute address values differ between runs (random entropy per Refresh); only the inequality within a single run is asserted</span></li> </ul> </div> </div> </div> <!-- TEST 3 --> <div class="test-card"> <div class="test-card-header"> <div class="test-number">03</div> <div class="test-title-wrap"> <div class="test-id">Account Level</div> <div class="test-title">Different <code>account</code> produces a different address</div> </div> </div> <div class="test-body"> <div> <div class="sub-label">Objective</div> <p class="objective-text"> Verify that the BIP44 <em>account</em> level of the derivation path affects the output. Changing the account value while keeping all other parameters constant must produce a different address, confirming that the derivation path <code>m / purpose' / coin_type' / account' / change / index</code> is correctly parameterized. </p> </div> <div> <div class="sub-label">Preconditions</div> <div class="precond-list"> <span class="precond-chip">App launched &amp; ready</span> <span class="precond-chip">Wallet tab active</span> <span class="precond-chip">Mode: HD Wallet</span> <span class="precond-chip">Blockchain: Bitcoin</span> </div> </div> <div> <div class="sub-label">Steps</div> <table class="steps-table"> <thead><tr><th>#</th><th>Action</th><th>Method</th></tr></thead> <tbody> <tr><td class="step-num">1</td><td class="step-action">Set <code>address index=5</code>, passphrase to <code>"my secret passphrase"</code></td><td class="step-method">setFieldValue + setBip39Passphrase</td></tr> <tr><td class="step-num">2</td><td class="step-action">Set <code>account=0</code>, click Refresh → <code>addressAccount0</code></td><td class="step-method">setFieldValue + clickRefresh + getDisplayedAddress</td></tr> <tr><td class="step-num">3</td><td class="step-action">Set <code>account=2</code>, click Refresh → <code>addressAccount2</code></td><td class="step-method">setFieldValue + clickRefresh + getDisplayedAddress</td></tr> </tbody> </table> </div> <div> <div class="sub-label">Expected Result</div> <div class="assertion-box ineq">addressAccount0 ≠ addressAccount2</div> </div> <div> <div class="sub-label">Failure Scenarios</div> <ul class="failure-list"> <li><span class="fail-bullet"></span><span class="fail-text"><strong>Both addresses equal</strong> — the account field change is not propagated to the BIP44 derivation engine</span></li> </ul> </div> </div> </div> <!-- TEST 4 --> <div class="test-card"> <div class="test-card-header"> <div class="test-number">04</div> <div class="test-title-wrap"> <div class="test-id">Address Index Level</div> <div class="test-title">Different <code>address index</code> produces a different address</div> </div> </div> <div class="test-body"> <div> <div class="sub-label">Objective</div> <p class="objective-text"> Verify that the BIP44 <em>address index</em> level of the derivation path affects the output. Changing the address index while keeping all other parameters constant must produce a different address, confirming that address enumeration within an account works correctly. </p> </div> <div> <div class="sub-label">Preconditions</div> <div class="precond-list"> <span class="precond-chip">App launched &amp; ready</span> <span class="precond-chip">Wallet tab active</span> <span class="precond-chip">Mode: HD Wallet</span> <span class="precond-chip">Blockchain: Bitcoin</span> </div> </div> <div> <div class="sub-label">Steps</div> <table class="steps-table"> <thead><tr><th>#</th><th>Action</th><th>Method</th></tr></thead> <tbody> <tr><td class="step-num">1</td><td class="step-action">Set <code>account=2</code>, passphrase to <code>"my secret passphrase"</code></td><td class="step-method">setFieldValue + setBip39Passphrase</td></tr> <tr><td class="step-num">2</td><td class="step-action">Set <code>address index=0</code>, click Refresh → <code>addressIndex0</code></td><td class="step-method">setFieldValue + clickRefresh + getDisplayedAddress</td></tr> <tr><td class="step-num">3</td><td class="step-action">Set <code>address index=5</code>, click Refresh → <code>addressIndex5</code></td><td class="step-method">setFieldValue + clickRefresh + getDisplayedAddress</td></tr> </tbody> </table> </div> <div> <div class="sub-label">Expected Result</div> <div class="assertion-box ineq">addressIndex0 ≠ addressIndex5</div> </div> <div> <div class="sub-label">Failure Scenarios</div> <ul class="failure-list"> <li><span class="fail-bullet"></span><span class="fail-text"><strong>Both addresses equal</strong> — the address index change is not propagated to the BIP44 derivation engine</span></li> </ul> </div> </div> </div> <!-- TEST 5 --> <div class="test-card"> <div class="test-card-header"> <div class="test-number">05</div> <div class="test-title-wrap"> <div class="test-id">Determinism</div> <div class="test-title">Same entropy + same params always produce the same address</div> </div> </div> <div class="test-body"> <div> <div class="sub-label">Objective</div> <p class="objective-text"> Verify that HD Wallet derivation is fully deterministic: given the same entropy, the same BIP39 passphrase, the same account and the same address index, the derived address is always identical across multiple derivations. </p> </div> <div> <div class="sub-label">Design Note</div> <div class="design-note"> <strong>Why Refresh cannot be used here:</strong> <code>clickRefresh()</code> regenerates <em>random</em> entropy on every call — two consecutive Refresh calls will always produce different addresses by design. This is not a bug.<br><br> Instead, this test injects a fixed 256-bit entropy value directly into <code>#entropy_id</code> and dispatches <code>input</code> + <code>change</code> events to trigger the BIP32 derivation waterfall, bypassing the random entropy generation step entirely. <span class="entropy-value">a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2</span> <span style="font-size:11px;color:#604888;display:block;margin-top:6px;">64 hex chars · 256 bits · valid BIP39 entropy</span> </div> </div> <div> <div class="sub-label">Preconditions</div> <div class="precond-list"> <span class="precond-chip">App launched &amp; ready</span> <span class="precond-chip">Wallet tab active</span> <span class="precond-chip">Mode: HD Wallet</span> <span class="precond-chip">Blockchain: Bitcoin</span> </div> </div> <div> <div class="sub-label">Steps</div> <table class="steps-table"> <thead><tr><th>#</th><th>Action</th><th>Method</th></tr></thead> <tbody> <tr><td class="step-num">1</td><td class="step-action">Set <code>account=2</code>, <code>index=5</code>, passphrase</td><td class="step-method">setFieldValue × 2 + setBip39Passphrase</td></tr> <tr><td class="step-num">2</td><td class="step-action">Inject fixed entropy → trigger derivation → <code>address1</code></td><td class="step-method">deriveFromFixedEntropy + getDisplayedAddress</td></tr> <tr><td class="step-num">3</td><td class="step-action">Re-inject same account, index, passphrase</td><td class="step-method">setFieldValue × 2 + setBip39Passphrase</td></tr> <tr><td class="step-num">4</td><td class="step-action">Inject same fixed entropy again → <code>address2</code></td><td class="step-method">deriveFromFixedEntropy + getDisplayedAddress</td></tr> </tbody> </table> </div> <div> <div class="sub-label">Expected Result</div> <div class="assertion-box eq">address1 = address2 (non-empty)</div> </div> <div> <div class="sub-label">Failure Scenarios</div> <ul class="failure-list"> <li><span class="fail-bullet"></span><span class="fail-text"><strong>address1 ≠ address2</strong> — derivation is not deterministic; serious BIP32 implementation bug</span></li> <li><span class="fail-bullet"></span><span class="fail-text"><strong>Empty address</strong> — derivation waterfall not triggered by the <code>change</code> event on <code>#entropy_id</code></span></li> </ul> </div> </div> </div> </div><!-- /tests-grid --> <!-- ── SUMMARY ── --> <div class="section-title">Summary</div> <div class="context-block" style="padding:0;overflow:hidden;"> <table class="summary-table"> <thead> <tr><th>#</th><th>Title</th><th>Assertion</th><th>Blockchain</th></tr> </thead> <tbody> <tr> <td style="font-family:var(--mono);color:var(--muted);">01</td> <td>Bitcoin address format validation</td> <td><span class="badge badge-regex">regex</span></td> <td><span class="badge badge-btc">Bitcoin</span></td> </tr> <tr> <td style="font-family:var(--mono);color:var(--muted);">02</td> <td>Passphrase modifies the derived address</td> <td><span class="badge badge-ineq">inequality</span></td> <td><span class="badge badge-btc">Bitcoin</span></td> </tr> <tr> <td style="font-family:var(--mono);color:var(--muted);">03</td> <td>Different account produces a different address</td> <td><span class="badge badge-ineq">inequality</span></td> <td><span class="badge badge-btc">Bitcoin</span></td> </tr> <tr> <td style="font-family:var(--mono);color:var(--muted);">04</td> <td>Different address index produces a different address</td> <td><span class="badge badge-ineq">inequality</span></td> <td><span class="badge badge-btc">Bitcoin</span></td> </tr> <tr> <td style="font-family:var(--mono);color:var(--muted);">05</td> <td>Same entropy + same params → same address (determinism)</td> <td><span class="badge badge-eq">equality</span></td> <td><span class="badge badge-btc">Bitcoin</span></td> </tr> </tbody> </table> </div> </main> </body> </html>