Anthony Towns [ARCHIVE] on Nostr: 📅 Original date posted:2018-03-27 📝 Original message:On Wed, Mar 21, 2018 at ...
📅 Original date posted:2018-03-27
📝 Original message:On Wed, Mar 21, 2018 at 05:47:01PM -0700, Bram Cohen via bitcoin-dev wrote:
> [...] Most unused opcodes should be reclaimed as RETURN_VALID,
> but there should still be one OP_NOP and there should be a 'real' RETURN_VALID,
> which (a) is guaranteed to not be soft forked into something else in the
> future, and (b) doesn't have any parsing weirdness.
What's the reason for those? I could see an argument for RETURN_VALID, I guess:
confA IF condB IF condC IF [pathA] RETURN_VALID ENDIF ENDIF ENDIF [pathB]
is probably simpler and saves 3 bytes compared to:
1 condA IF condB IF condC IF [pathA] NOT ENDIF ENDIF ENDIF IF [pathB] ENDIF
but that doesn't seem crazy compelling? I don't see a reason to just keep
one OP_NOP though.
> The parsing weirdness of
> all the unclaimed opcodes is interesting. Because everything in an IF clause
> needs to be parsed in order to find where the ELSE is, you have a few options
> for dealing with an unknown opcode getting parsed in an unexecuted section of
> code. They are (a) avoid the problem completely by exterminating IF and MASTing
> (b) avoid the problem completely by getting rid of IF and adding IFJUMP,
> IFNJUMP, and JUMP which specify a number of bytes (this also allows for script
> merkleization) (c) require all new opcodes have fixed length 1, even after
> they're soft forked, (d) do almost like (c) but require that on new soft forks
> people hack their old scripts to still parse properly by avoiding the OP_ELSE
> in inopportune places (yuck!) (e) make it so that the unknown opcodes case a
> RETURN_VALID even when they're parsed, regardless of whether they're being
> executed.
I was figuring (c), fwiw, and assuming that opcodes will just be about
manipulating stack values and marking the script as invalid, rather than,
say, introducing new flow control ops.
> By far the most expedient option is (e) cause a RETURN_VALID at parse time.
> There's even precedent for this sort of behavior in the other direction with
> disabled opcodes causing failure at parse time even if they aren't being
> executed.
You're probably right. That still doesn't let you implement intercal's
COMEFROM statement as a new opcode, of course. :)
> A lot can be said about all the options, but one thing I feel like snarking
> about is that if you get rid of IFs using MAST, then it's highly unclear
> whether OP_DEPTH should be nuked as well. My feeling is that it should and that
> strict parsing should require that the bottom thing in the witness gets
> referenced at some point.
I guess when passing the script you could perhaps check if each witness
item could have been replaced with OP_FALSE or OP_1 and still get the
same result, and consider the transaction non-standard if so?
> Hacking in a multisig opcode isn't a horrible idea, but it is very stuck
> specifically on m-of-n and doesn't support more complex formulas for how
> signatures can be combined, which makes it feel hacky and weird.
Hmm? The opcode I suggested works just as easily with arbitrary formulas,
eg, "There must be at least 1 signer from pka{1,2,3}, and 3 signers all
up, except each of pkb{1,2,3,4,5,6} only counts for half":
0 pkb6 pkb5 pkb4 pkb3 pkb2 pkb1 pka3 pka2 pka1 9 CHECK_AGGSIG_VERIFY
(declare pubkeys)
0b111 CHECK_AGG_SIGNERS VERIFY
(one of pka{1,2,3} must sign)
0b001 CHECK_AGG_SIGNERS
0b010 CHECK_AGG_SIGNERS ADD
0b100 CHECK_AGG_SIGNERS ADD
DUP ADD
(pka{1,2,3} count double)
0b000001000 CHECK_AGG_SIGNERS ADD
0b000010000 CHECK_AGG_SIGNERS ADD
0b000100000 CHECK_AGG_SIGNERS ADD
0b001000000 CHECK_AGG_SIGNERS ADD
0b010000000 CHECK_AGG_SIGNERS ADD
0b100000000 CHECK_AGG_SIGNERS ADD
(pkb{1..6} count single)
6 EQUAL
(summing to a total of 3 doubled)
Not sure that saves it from being "hacky and weird" though...
(There are different ways you could do "CHECK_AGG_SIGNERS": for
instance, take a bitmask of keys and return the bitwise-and with the
keys that signed, or take a bitmask and just return the number of keys
matching that bitmask that signed, or take a pubkey index and return a
boolean whether that key signed)
Cheers,
aj
Published at
2023-06-07 18:11:20Event JSON
{
"id": "4e4534f1e86c86c2199e09ce47a33761c8f6a1f5501357ae6d9212f85a5660bd",
"pubkey": "f0feda6ad58ea9f486e469f87b3b9996494363a26982b864667c5d8acb0542ab",
"created_at": 1686161480,
"kind": 1,
"tags": [
[
"e",
"c642cb03cf53d09173f83ef94f54b92b8b7adc0fc71062ee502c68afe8983be5",
"",
"root"
],
[
"e",
"77e3c460d8cf8829f5e712d016f3e5fa410b175e7da061a4f4ae09f42ef35166",
"",
"reply"
],
[
"p",
"fb7007c42a06687e3cd3fbbb1a3b17972e2a949ae679445f6b96579114d05cd9"
]
],
"content": "📅 Original date posted:2018-03-27\n📝 Original message:On Wed, Mar 21, 2018 at 05:47:01PM -0700, Bram Cohen via bitcoin-dev wrote:\n\u003e [...] Most unused opcodes should be reclaimed as RETURN_VALID,\n\u003e but there should still be one OP_NOP and there should be a 'real' RETURN_VALID,\n\u003e which (a) is guaranteed to not be soft forked into something else in the\n\u003e future, and (b) doesn't have any parsing weirdness.\n\nWhat's the reason for those? I could see an argument for RETURN_VALID, I guess:\n\n confA IF condB IF condC IF [pathA] RETURN_VALID ENDIF ENDIF ENDIF [pathB]\n\nis probably simpler and saves 3 bytes compared to:\n\n 1 condA IF condB IF condC IF [pathA] NOT ENDIF ENDIF ENDIF IF [pathB] ENDIF\n\nbut that doesn't seem crazy compelling? I don't see a reason to just keep\none OP_NOP though.\n\n\u003e The parsing weirdness of\n\u003e all the unclaimed opcodes is interesting. Because everything in an IF clause\n\u003e needs to be parsed in order to find where the ELSE is, you have a few options\n\u003e for dealing with an unknown opcode getting parsed in an unexecuted section of\n\u003e code. They are (a) avoid the problem completely by exterminating IF and MASTing\n\u003e (b) avoid the problem completely by getting rid of IF and adding IFJUMP,\n\u003e IFNJUMP, and JUMP which specify a number of bytes (this also allows for script\n\u003e merkleization) (c) require all new opcodes have fixed length 1, even after\n\u003e they're soft forked, (d) do almost like (c) but require that on new soft forks\n\u003e people hack their old scripts to still parse properly by avoiding the OP_ELSE\n\u003e in inopportune places (yuck!) (e) make it so that the unknown opcodes case a\n\u003e RETURN_VALID even when they're parsed, regardless of whether they're being\n\u003e executed.\n\nI was figuring (c), fwiw, and assuming that opcodes will just be about\nmanipulating stack values and marking the script as invalid, rather than,\nsay, introducing new flow control ops.\n\n\u003e By far the most expedient option is (e) cause a RETURN_VALID at parse time.\n\u003e There's even precedent for this sort of behavior in the other direction with\n\u003e disabled opcodes causing failure at parse time even if they aren't being\n\u003e executed.\n\nYou're probably right. That still doesn't let you implement intercal's\nCOMEFROM statement as a new opcode, of course. :)\n\n\u003e A lot can be said about all the options, but one thing I feel like snarking\n\u003e about is that if you get rid of IFs using MAST, then it's highly unclear\n\u003e whether OP_DEPTH should be nuked as well. My feeling is that it should and that\n\u003e strict parsing should require that the bottom thing in the witness gets\n\u003e referenced at some point.\n\nI guess when passing the script you could perhaps check if each witness\nitem could have been replaced with OP_FALSE or OP_1 and still get the\nsame result, and consider the transaction non-standard if so?\n\n\u003e Hacking in a multisig opcode isn't a horrible idea, but it is very stuck\n\u003e specifically on m-of-n and doesn't support more complex formulas for how\n\u003e signatures can be combined, which makes it feel hacky and weird.\n\nHmm? The opcode I suggested works just as easily with arbitrary formulas,\neg, \"There must be at least 1 signer from pka{1,2,3}, and 3 signers all\nup, except each of pkb{1,2,3,4,5,6} only counts for half\":\n\n 0 pkb6 pkb5 pkb4 pkb3 pkb2 pkb1 pka3 pka2 pka1 9 CHECK_AGGSIG_VERIFY\n (declare pubkeys)\n 0b111 CHECK_AGG_SIGNERS VERIFY\n (one of pka{1,2,3} must sign)\n 0b001 CHECK_AGG_SIGNERS\n 0b010 CHECK_AGG_SIGNERS ADD\n 0b100 CHECK_AGG_SIGNERS ADD\n DUP ADD\n (pka{1,2,3} count double)\n 0b000001000 CHECK_AGG_SIGNERS ADD\n 0b000010000 CHECK_AGG_SIGNERS ADD\n 0b000100000 CHECK_AGG_SIGNERS ADD\n 0b001000000 CHECK_AGG_SIGNERS ADD\n 0b010000000 CHECK_AGG_SIGNERS ADD\n 0b100000000 CHECK_AGG_SIGNERS ADD\n (pkb{1..6} count single)\n 6 EQUAL\n (summing to a total of 3 doubled)\n\nNot sure that saves it from being \"hacky and weird\" though...\n\n(There are different ways you could do \"CHECK_AGG_SIGNERS\": for\ninstance, take a bitmask of keys and return the bitwise-and with the\nkeys that signed, or take a bitmask and just return the number of keys\nmatching that bitmask that signed, or take a pubkey index and return a\nboolean whether that key signed)\n\nCheers,\naj",
"sig": "f1b7e803a061a184efb32e67d13c12c62d4958c035fe723d5a1d525fdd49eda3ddb019de3124210721a7ac5e8a633a648590cefd1ca837e2ae9f65fd319032da"
}