Repository navigation
Conversation
- Encode this.age durations with time units as BIP68 512-second chunks (rounded up)
- Reject compile-time this.age values that are not a valid BIP68 relative timelock
- Check runtime this.age values when the contract is spent
- Reject tx.time values outside the 32-bit locktime range, or written as a duration
- Accept { blocks } or { seconds } as the sequence input option, and reject raw sequence numbers with ignored BIP68 bits
- Show libauth's reason when a require statement fails for another reason than a false condition
- Add end-to-end sequence number tests and update the timelock docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## next #455 +/- ##
==========================================
+ Coverage 89.34% 89.86% +0.51%
==========================================
Files 61 62 +1
Lines 5048 5148 +100
Branches 942 977 +35
==========================================
+ Hits 4510 4626 +116
+ Misses 413 407 -6
+ Partials 125 115 -10 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
A this.age operand that refers to a global int constant was validated at compile time, but the validated literal still referred to its constant, so it was lowered to a constant function call and code generation added the runtime check as well. Block-count constants such as `this.age >= WAIT_BLOCKS` now compile to a plain push, the same as a literal. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
On behalf of Mathieu G. (mr-zwets), drafted with Claude Opus 5.5. Pushed a small follow-up, 61c1982. A Cause: Added an With this, the locktime findings from the 0.14 audit look addressed to us. |
…ive timelocks
The time-unit flag was kept through every folded operation, so a global
constant such as `1 days / 10 minutes * 7` (a number of blocks) was encoded
as a duration in seconds, and `4194304 + (1 days + 511) / 512` (already
encoded chunks) was encoded again. The flag now follows the units: sums and
multiples of durations are durations, a ratio of two durations is a plain
number, and a duration combined with a plain number is rejected in this.age,
and in tx.time below 500,000,000.
A `sequence` option without `blocks` or `seconds` (e.g. `{}` from plain
JavaScript) was encoded as a final sequence number, which silently disabled
the transaction's locktime. It is now rejected.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
On behalf of Mathieu G. (mr-zwets), drafted with Claude Opus 5.5. Pushed another follow-up, 392d481, for two issues from our pre-release audit:
|
|
On behalf of Mathieu G. (mr-zwets), drafted with Claude Opus 5.5. One remaining area from our audit: #455 encodes time units when the
Tradeoffs. Time units have real potential: durations read far better than raw numbers, #455 now encodes them correctly as BIP68 where it can, and the compiler could catch mixing seconds with block counts, which is an easy mistake. But treating them specially raises questions that go beyond timelocks:
Handling all of that is effectively a unit type in the type system. The alternatives are to keep units as plain seconds everywhere except where they are encoded, which is simpler but leaves the cases above as documented footguns, or to restrict where time-unit literals may appear. 392d481 only applies these rules to folded global constants, where #455 already tracked units; it does not follow units any further. |
Addresses the remaining (locktime-related) findings from the 0.14 audit.
Compiler
this.agedurations: time units (e.g.require(this.age >= 30 days)) were compared as a number of blocks. Durations are now encoded as BIP68 512-second chunks with the type flag, rounded up so the timelock is never shorter than written. This also works for global constants holding a duration (int literals now track whether they were written with a time unit, including through constant folding).this.agevalues: values that are not a valid BIP68 relative timelock are now a compile error (InvalidTimelockError). Valid values are a number of blocks (0–65535), a duration of up to 33,553,920 seconds (~388 days), or an already-encoded BIP68 value. A time unit inside a largerthis.ageexpression (e.g.period + 1 days) is also rejected, since it can't be encoded at compile time.this.agevalues (e.g. a constructor parameter) get a 10-byte runtime check beforeOP_CHECKSEQUENCEVERIFY:(x >> 16) * ((x >> 16) - 64) == 0, i.e. only BIP68 blocks or 512-second chunks. It has its own require message for debugging. There is no compiler option to disable it.tx.timeliterals: values outside0..4294967295, and durations below 500,000,000 (e.g.tx.time >= 2 hours, the bug in the docs example), are now a compile error.this.agecheck now enforces a non-final sequence number, anyrequire(this.age >= ...)counts as coveringtx.locktime. Runtimethis.agevalues no longer cause an extra injected guard.SDK
sequenceaccepts{ blocks }or{ seconds }, encoded withencodeBip68(which now rounds seconds up, matching the compiler).sequencethat enables a relative timelock but sets bits BIP68 ignores is rejected. Values with the disable flag (including the default0xfffffffe) pass unchanged.FailedRequireErrornow shows libauth's reason when a require fails for another reason than a false condition, e.g. a missing sequence number, an incompatible locktime type, or an invalid VM number.it.todo('test sequence numbers')with mocknet end-to-end tests. The mock network doesn't check the UTXO's age, so these are skipped on chipnet.Docs
globals.md:this.age/tx.timesections describe the encoding, limits and SDK usage. The "not supported to usethis.ageas second chunks" sentence is removed.contracts.md: the constants example now usesthis.age >= EXTENDED_TIMEOUT.transaction-builder.md: documents theInputOptionschanges.this.age >= 180 daysexample is now correct without changes.Bytecode impact
this.ageget a different (correct) constant.this.agevalue get the 10-byte check.this.agecheck withtx.locktimelose the injected guard.this.ageliterals or withtx.timeare unchanged.🤖 Generated with Claude Code