Hírolvasó
[$] Weighted interleaving for memory tiering
The path toward a no-GIL Python
Phase I: Experimental phase, which can start immediately, in which the free-threaded build is enabled through a build-time option. This should not be the default install anywhere. At least one major Python release should include this experimental free-threaded build, to allow third-party packages to test and do their own experimentation. In this stage we should make it clear the build is experimental, not supported for “production use”, and may be reverted.
Seven stable kernel updates
Security updates for Wednesday
[$] Home Assistant: ten years of privacy-focused home automation
Firefox 119.0 released
Security updates for Tuesday
OpenBSD's built-in memory leak detection
As announced on the misc@ mailing list, Otto Moerbeek (otto@), the author of OpenBSD's malloc(3) implementation [a.k.a. "otto malloc"], has written a tutorial on the new malloc(3) leak detection available in OpenBSD 7.4
Read it at: OpenBSD's built-in memory leak detection
Since the publication of that write-up, Otto has committed further enhancements:
CVSROOT: /cvs Module name: src Changes by: otto@cvs.openbsd.org 2023/10/22 06:19:26 Modified files: lib/libc/stdlib: malloc.3 malloc.c Log message: When option D is active, store callers for all chunks; this avoids the 0x0 call sites for leak reports. Also display more info on detected write of free chunks: print the info about where the chunk was allocated, and for the preceding chunk as well. ok asou@2023 Linux Foundation TAB election call for nominees
The TAB exists to provide advice from the kernel community to the Linux Foundation; it also serves to facilitate interactions both within the community and with outside entities. Over the last year, the TAB has overseen the organization of the Linux Plumbers Conference, released a kernel contribution maturity model for organizations, advised on code-of-conduct issues, and more.
Nominations should be sent in by November 13.
[$] Hyphens, minus, and dashes in Debian man pages
Security updates for Monday
Kernel prepatch 6.6-rc7
Anyway, while this is all bigger than I'd have liked it to be, if the upcoming week is quiet and normal, this is the last rc and next Sunday will see the final release and then we'll open the merge window for 6.7. I simply am not aware of any issues that would be showstoppers.
Rusty Russell: Covenants: Dealing with Amounts in Bitcoin Script
Covenants are a construction to allow introspection: a transaction output can place conditions on the transaction which spends it (beyond the specific “must provide a valid signature of itself and a particular pubkey”).
I previously looked at Examining ScriptPubkeys, but another useful thing covenants want to enforce is amounts. This is easy for equality, but consider the case where you are allowed to merge inputs: perhaps the first output amount must be the sum of the first and second inputs.
The problem is that Bitcoin Script deals in signed ones-complement values, and 31 bits limits us to 21.47483648 bitcoin. However, using OP_MULTISHA256 or OP_CAT, it’s possible to deal with full amounts. I’ve written some (untested!) script code below.
The Vexing Problem of AmountsUsing OP_TXHASH, we can get SHA256(input amount) and SHA256(output amount) on the stack. Since this involves hashing, we can’t evaluate the number for anything but equality, so as in other cases where we don’t have Fully Complete Covenants we need to have the user supply the actual values on the witness stack, and we test those for the conditions we want, and then make sure they match what OP_TXHASH says is in the transaction. I usually object to this backwards form (just give me the value on the stack!), but as you’ll see, we couldn’t natively use 64 bit values from OP_TX anyway (I had proposed pushing two values, which is its own kind of ugly).
A Value Form Bitcoin Script Can Deal With21M BTC is just under 2^51 satoshis.
We split these bits into a pair of stack values:
- lower 24 bits
- upper bits (27, but we allow up to 31)
I call this tuple “Script-friendly pair” (SFP) form. Note that all script numbers on stack are represented in little-endian, with a sign bit (0x80 on the last byte). This is a nasty format to work with, unfortunately.
Converting A Script-Friendly Pair to an 8-byte Little-Endian ValueHere’s the code to takes a positive CScriptNum, and produces two stack values which can be concatenated to make a 4 byte unsigned value:
# !UNTESTED CODE! # Stack (top to bottom): lower, upper OP_SWAP # Generate required prefix to append to stack value to make it 4 bytes long. OP_SIZE OP_DUP OP_NOTIF # 0 -> 00000000 OP_DROP 4 OP_PUSHDATA1 0x00 0x00 0x00 0x00 OP_ELSE OP_DUP 1 OP_EQUAL OP_IF # Single byte: prepend 0x00 0x00 0x00 OP_DROP 3 OP_PUSHDATA1 0x00 0x00 0x00 OP_ELSE OP_DUP 2 OP_EQUAL OP_IF # Two bytes: prepend 0x00 0x00 2 OP_PUSHDATA1 0x00 0x00 OP_ELSE 3 OP_EQUAL OP_IF # Three bytes: prepend 0x00 1 OP_PUSHDATA1 0x00 OP_ELSE # Prepend nothing. 0 OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_SWAP # Stack (top to bottom): upper, pad, lowerThat 46 bytes handles upper. Now lower is a CScriptNum between 0 and 16777215, and we want to produce two stack values which can be concatenated to make an 3 byte unsigned value. Here we have to remove the zero-padding in the four-byte case:
# !UNTESTED CODE! # Stack (top to bottom): upper, pad, lower OP_ROT # Generate required prefix to append to stack value to make it 3 bytes long. OP_SIZE OP_DUP OP_NOTIF # 0 -> 000000 OP_DROP 3 OP_PUSHDATA1 0x00 0x00 0x00 OP_ELSE OP_DUP 1 OP_EQUAL OP_IF # Single byte: prepend 0x00 0x00 OP_DROP 2 OP_PUSHDATA1 0x00 0x00 OP_ELSE OP_DUP 2 OP_EQUAL OP_IF # Two bytes. Now maybe final byte is 0x00 simply so it doesn't # appear negative, but we don't care. 1 OP_PUSHDATA1 0x00 OP_ELSE # Three bytes: empty append below 3 OP_EQUAL OP_NOTIF # Four bytes, e.g. 0xff 0xff 0xff 0x00 # Convert to three byte version: negate and add 2^23 # => 0xff 0xff 0xff OP_NEG 4 OP_PUSHDATA1 0x00 0x00 0x80 0x00 OP_ADD OP_ENDIF # Prepend nothing. 0 OP_ENDIF OP_ENDIF OP_ENDIF OP_SWAP # Stack (top to bottom): lower, pad, upper, padYou can optimize these 47 bytes a little, but I’ll leave that as an exercise for the reader!
Now we use OP_MULTISHA256 (or OP_CAT 3 times and OP_SHA256) to concatentate them to form an 8-byte little-endian number, for comparison against the format used by OP_TXHASH.
Basically, 95 bytes to compare our tuple to a hashed value.
Adding Two Script-Friendly PairsLet’s write some code to add two well-formed Script-Friendly Pairs!
# !UNTESTED CODE! # Stack (top to bottom): a_lower, a_upper, b_lower, b_upper OP_ROT OP_ADD OP_DUP 4 OP_PUSHDATA1 0x00 0x00 0x00 0x01 OP_GREATERTHANOREQUAL OP_IF # lower overflow, bump upper. # FIXME: We can OP_TUCK this constant above! 4 OP_PUSHDATA1 0x00 0x00 0x00 0x01 OP_SUB OP_SWAP OP_1ADD OP_ELSE OP_SWAP OP_ENDIF # Stack now: a_upper(w/carry), lower_sum, b_upper. OP_ROT OP_ADD OP_SWAP # Stack now: lower_sum, upper_sumNote that these 26 bytes don’t check that upper doesn’t overflow: if we’re dealing with verified amounts, we can add 16 times before it’s even possible (and it’s never possible with distinct amounts of course). Still, we can add OP_DUP 0 OP_GREATERTHANOREQUAL OP_VERIFY before the final OP_SWAP.
Checking Script-Friendly PairsThe code above assumes well-formed pairs, but since the pairs will come from the witness stack, we need to have a routine to check that a pair is wel-formed:
# !UNTESTED CODE! # Stack: lower, upper OP_DUP # lower must be 0 - 0xFFFFFF inclusive 0 4 OP_PUSHDATA1 0xFF 0xFF 0xFF 0x00 OP_WITHIN OP_VERIFY OP_OVER # upper must be 0 - 0x7FFFFFF inclusive 0 4 OP_PUSHDATA1 0xFF 0xFF 0xFF 0x07 OP_WITHIN OP_VERIFYThis ensures the ranges are all within spec: no negative numbers, no giant numbers.
SummaryWhile this shows that OP_CAT/OP_MULTISHA256 is sufficient to deal with bitcoin amounts in Script, the size (about 250 bytes to validate that two inputs equals one output) makes a fairly compelling case for optimization.
It’s worth noting that this is why Liquid chose to add the following 64-bit opcodes to bitscoin script: OP_ADD64, OP_SUB64, OP_MUL64, OP_DIV64, OP_NEG64, OP_LESSTHAN64, OP_LESSTHANOREQUAL64, OP_GREATERTHAN64, OP_GREATERTHANOREQUAL64.
(They also reenabled the bitwise opcodes (OP_XOR etc) to work just fine with these. They also implemented OP_SCRIPTNUMTOLE64, OP_LE64TOSCRIPTNUM and OP_LE32TOLE64 for conversion.)
In my previous post I proposed OP_LESS which works on arbitrary values, which doen’t work for these because the endian is wrong! As a minimum, we’d need to add OP_LESSTHAN64, OP_ADD64 and OP_NEG64 to allow 64-bit comparison, addition and subtraction.
But, with only OP_CAT or OP_MULTISHA256, it’s possible to deal with amounts. It’s just not pretty!
Thanks for reading!
[$] mseal() and what comes after
Security updates for Friday
Three stable kernel updates
[$] Toward safer GNU C Library tunable handling
Security updates for Thursday
Rusty Russell: Covenants: Examining ScriptPubkeys in Bitcoin Script
Covenants are a construction to allow introspection: a transaction output can place conditions on the transaction which spends it (beyond the specific “must provide a valid signature of itself and a particular pubkey”).
My preferred way of doing instrospection is for Bitcoin Script have a way of asking for various parts of the transaction onto the stack (aka OP_TX) for direct testing (Fully Complete Covenants, as opposed to using some tx hash, forcing the Script to produce a matching hash to pass (Equality Covenants). In the former case, you do something like:
# Is the nLocktime > 100? OP_TX_BIT_NLOCKTIME OP_TX 100 OP_GREATERTHAN OP_VERIFYIn the latter you do something like:
# They provide nLocktime on the stack. OP_DUP # First check it's > 100 100 OP_GREATERTHAN OP_VERIFY # Now check it's actually the right value, by comparing its hash the hash of nLocktime OP_SHA256 OP_TX_BIT_NLOCKTIME OP_TXHASH OP_EQUALVERIFYHowever, when we come to examining an output’s ScriptPubkey, we’re forced into the latter mode unless we’re seeking an exact match: the ScriptPubkey is (almost always) a one-way function of the actual spending conditions.
Making a Simple Taproot, in ScriptLet’s take a simple taproot case. You want to assert that the scriptPubkey pays to a known key K, or a script given by the covenent spender. This is the simplest interesting form of Taproot, with a single script path.
The steps to make this into a ScriptPubkey (following BIP 341) are:
- Get a tagged tapleaf hash of the script
- Tweak the key K by this value.
- Prepend two bytes “0x51 0x20”.
- Compare with the ScriptPubkey of this tx.
If we spell out the things we need to hash, it looks like:
SHA256(SHA256("TapLeaf") + SHA256("TapLeaf") + 0xC0 + CSCRIPTNUM(LEN(script)) + script)CSCRIPTNUM(X) is (if X is in canonical form, as it will be from OP_SIZE):
- if X is less than 253:
- X
- otherwise, if the length is less than 256:
- 0xFD 0x00 X
- otherwise, if the length is less than 65536:
- 0xFD X
- otherwise, we don’t care, make shorter scripts!
The obvious way to do this is to enable OP_CAT, but this was removed because it allows construction of giant stack variables. If that is an issue, we can instead use a “concatenate-and-hash” function OP_MULTISHA256, which turns out to be easiest to use if it hashes the stack from top to bottom.
OP_MULTISHA256 definition:
- If the stack is empty, fail.
- Pop N off the stack.
- If N is not a CScriptNum, fail.
- If there are fewer than N entries on the stack, fail.
- Initialize a SHA256 context.
- while N > 0:
- Pop the top entry off the stack.
- Hash it into the SHA256 context
- Decrement N
- Finish the SHA256 context, and push the resulting 32 bytes onto the stack.
The result is either:
# Script is on stack, produce tagged tapleaf hash # First, encode length OP_SIZE OP_DUP # < 253? OP_PUSHDATA1 1 253 OP_LESSTHAN OP_IF # Empty byte on stack: 0 OP_ELSE OP_DUP # > 255? OP_PUSHDATA1 1 0xFF OP_GREATERTHAN OP_IF OP_PUSHDATA1 1 0xFD OP_ELSE # Needs padding byte OP_PUSHDATA1 2 0xFD 0x00 OP_ENDIF OP_ENDIF # Push 0xC0 leaf_version on stack OP_PUSHDATA1 1 0xC0 # Push hashed tag on stack, twice. OP_PUSHDATA1 7 "TapLeaf" OP_SHA256 OP_DUP # Now, hash them together 6 OP_MULTISHA256Or, using OP_CAT (assuming it also concatenates the top of stack to second on stack):
# Script is on stack, produce tagged tapleaf hash # First, encode length OP_SIZE OP_DUP # < 253? OP_PUSHDATA1 1 253 OP_LESSTHAN OP_NOTIF OP_DUP # > 255? OP_PUSHDATA1 1 0xFF OP_GREATERTHAN OP_IF OP_PUSHDATA1 1 0xFD OP_ELSE # Needs padding byte OP_PUSHDATA1 2 0xFD 0x00 OP_ENDIF OP_CAT OP_ENDIF # Prepend length to script OP_CAT # Prepend 0xC0 leaf_version OP_PUSHDATA1 1 0xC0 OP_CAT # Push hashed tag on stack, twice, and prepend OP_PUSHDATA1 7 "TapLeaf" OP_SHA256 OP_DUP OP_CAT OP_CAT # Hash the lot. OP_SHA256 Step 2: We need to Tweak a Key, OP_KEYADDTWEAKNow, we need to tweak a public key, as detailed in BIP 341:
def taproot_tweak_pubkey(pubkey, h): t = int_from_bytes(tagged_hash("TapTweak", pubkey + h)) if t >= SECP256K1_ORDER: raise ValueError P = lift_x(int_from_bytes(pubkey)) if P is None: raise ValueError Q = point_add(P, point_mul(G, t)) return 0 if has_even_y(Q) else 1, bytes_from_int(x(Q))Let’s assume OP_KEYADDTWEAK works like so:
- If there are less than two items on the stack, fail.
- Pop the tweak t off the stack. If t >= SECP256K1_ORDER, fail.
- Pop the key P off the stack. If it is not a valid compressed pubkey, fail. Convert to Even-Y if necessary. (i.e. lift_x()).
- Q = P + t*G.
- Push the X coordinate of Q on the stack.
So now we just need to create the tagged hash, and feed it to OP_KEYADDTWEAK:
# Key, tapscript hash are on stack. OP_OVER OP_PUSHDATA1 8 "TapTweak" OP_SHA256 OP_DUP # Stack is now: key, tapscript, key, H(TapTweak), H(TapTweak) 4 OP_MULTISHA256 OP_KEYADDTWEAKOr with OP_CAT instead of OP_MULTISHA256:
# Key, tapscript hash are on stack. OP_OVER OP_PUSHDATA1 8 "TapTweak" OP_SHA256 OP_DUP # Stack is now: key, tapscript, key, H(TapTweak), H(TapTweak) OP_CAT OP_CAT OP_CAT OP_SHA256 OP_KEYADDTWEAK Step 3: We Need To Prepend The Taproot BytesThis is easy with OP_CAT:
# ScriptPubkey, Taproot key is on stack. # Prepend "OP_1 32" to make Taproot v1 ScriptPubkey OP_PUSHDATA1 2 0x51 0x20 OP_CAT OP_EQUALVERIFYWith OP_MULTISHA256 we need to hash the ScriptPubkey to compare it (or, if we only have OP_TXHASH, it’s already hashed):
# ScriptPubkey, Taproot key is on stack. OP_SHA256 # Prepend "OP_1 32" to make Taproot v1 ScriptPubkey OP_PUSHDATA1 2 0x51 0x20 2 OP_MULTISHA256 # SHA256(ScriptPubkey) == SHA256(0x51 0x20 taproot) OP_EQUALVERIFY Making a More Complete Taproot, in ScriptThat covers the “one key, one script” case.
If we have more than one taproot leaf, we need to perform the merkle on them, rather than simply use the taproot leaf directly. Let’s assume for simplicity that we have two scripts:
- Produce the tagged leaf hash for scripts, call them H1 and H2.
- If H1 < H2, merkle is TaggedHash("TapBranch", H1 + H2), otherwise TaggedHash("TapBranch", H2 + H1)
We’ve done this before, it’s just Step 1 as before.
Step 2: Compare and Hash: We Need OP_LESS or OP_CONDSWAPUnfortunately, all the arithmetic functions except OP_EQUAL only take CScriptNums, so we need a new opcode to compare 32-byte blobs. Minimally, this would be OP_LESS, though OP_CONDSWAP (put lesser one on top of stack) is possible too. In our case we don’t care what happens in unequal lengths, but if we assume big-endian values are most likely, we could zero-prepend to the shorter value before comparing.
The result looks like this:
# Hash1, Hash2 are on the stack. # Put lesser hash top of stack if not already OP_LESS OP_NOTIF OP_SWAP OP_ENDIF OP_PUSHDATA1 9 "TapBranch" OP_SHA256 OP_DUP 4 OP_MULTISHA256Or, using OP_CAT and OP_CONDSWAP:
# Hash1, Hash2 are on the stack. # Put lesser hash top of stack if not already OP_CONDSWAP OP_PUSHDATA1 9 "TapBranch" OP_SHA256 OP_DUP OP_CAT OP_CAT OP_CAT OP_SHA256So now we can make arbitrarily complex merkle trees from parts, in Script!
Making More Useful Templates: Reducing the Power of OP_SUCCESSAllowing the covenant spender to specify a script branch of their own is OK if we simply want a condition which is “… OR anything you want”. But that’s not generally useful: consider vaults, where you want to enforce a delay, after which they can spend. In this case, we want “… AND anything you want”.
We can, of course, insist that the script they provide starts with 1000 OP_CHECKSEQUENCEVERIFY. But because any unknown opcode causes immediate script success (without actually executing anything), they can override this test by simply inserting an invalid opcode in the remainder of the script!
There are two ways I can see to resolve this: one is delegation, where the remainder of the script is popped off the stack (OP_POPSCRIPT?). You would simply insist that the script they provide be exactly 1000 OP_CHECKSEQUENCEVERIFY OP_POPSCRIPT.
The other way is to weaken OP_SUCCESSx opcodes. This must be done carefully! In particular, we can use a separator, such as OP_SEPARATOR, and change the semantics of OP_SUCCESSx:
- If there is an OP_SEPARATOR before OP_SUCCESSx:
- Consider the part before the OP_SEPARATOR:
- if (number of OP_IF) + (number of OP_NOTIF) > (number of OP_ENDIF): fail
- Otherwise execute it as normal: if it fails, fail.
- Consider the part before the OP_SEPARATOR:
- Succeed the script
This insulates a prefix from OP_SUCCESSx, but care has to be taken that it is a complete script fragment: a future OP_SUCCESSx definition must not turn an invalid script into a valid one (by revealing an OP_ENDIF which would make the script valid).
SummaryI’ve tried to look at what it would take to make generic convenants in Script: ones which can meaningfully interrogate spending conditions assuming some way (e.g. OP_TXHASH) of accessing an output’s script. There are reasons to believe this is desirable (beyond a completeness argument): vaulting in particular requires this.
We need three new Script opcodes: I’ve proposed OP_MULTISHA256, OP_KEYADDTWEAK and OP_LESS, and a (soft-fork) revision to treatment of OP_SUCCESSx. None of these are grossly complex.
The resulting scripts are quite long (and mine are untested and no doubt buggy!). It’s 41 bytes to hash a tapleaf, 19 to combine two tapleaves, 8 to compare the result to the scriptpubkey. That’s at least 109 witness weight to do a vault, and in addition you need to feed it the script you’re using for the output. That seems expensive, but not unreasonable: if this were to become common then new opcodes could combine several of these steps.
I haven’t thought hard about the general applicability of these opcodes, so there may be variants which are better when other uses are taken into account.
Thanks for reading!