Skip to content

Conversation

@sipa
Copy link
Member

@sipa sipa commented Oct 16, 2014

  • Make CScript::operator<<(const std::vector&) cover all push operations (including OP_0, OP_1..OP_16 and OP_1NEGATE), and make the push for numbers use that.
  • Convert the uint256, uint160 and CPubKey into a single templated one using the vector push code.
  • Get rid of the int64_t operator<<; better be explicit about what needs pushing, so change all number pushes to use CScriptNum (which, due to the generic vector push now also correctly uses OP_n).
  • Get rid of some unused methods and constructors as a result.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the number push operator doesn't work anymore - it uses OP_4 intead of 1-byte push of 0x04. As this is one of the very few cases where you need the byte-level ability, I figured it's just shorter to turn it into a single byte sequence here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, ok then...maybe put in a comment of the old code?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the old code matter? The coinbase input is just a byte array.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose for historical nostalgia? In any case, it matters not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@sipa sipa force-pushed the unipush branch 2 times, most recently from 06d2bca to 4ad85ab Compare October 16, 2014 06:08
@TheBlueMatt
Copy link
Contributor

utACK...hope no one ever writes a class with unsigned char* begin() and unsigned char* end()

@laanwj
Copy link
Member

laanwj commented Oct 16, 2014

hope no one ever writes a class with unsigned char* begin() and unsigned char* end()

Why not? That would still be transparently converted to a std::vector, right?

@TheBlueMatt
Copy link
Contributor

OK, hope no one ever writes a class with unsigned char* begin() and
unsigned char* end() and doesnt make those map to a consecutive memory
region they want turned into a vector :)

On 10/16/14 06:46, Wladimir J. van der Laan wrote:

hope no one ever writes a class with unsigned char* begin() and
unsigned char* end()

Why not? That would still be transparently converted to a std::vector,
right?


Reply to this email directly or view it on GitHub
#5091 (comment).

@sipa
Copy link
Member Author

sipa commented Oct 17, 2014

Rebased on top of #4981.

@sipa
Copy link
Member Author

sipa commented Oct 17, 2014

Also added a move-only commit to move a lot of the larger script methods to the .cpp file.

@TheBlueMatt
Copy link
Contributor

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

utACK commithash c5a758d7cd63c2b3bbfd8daef5d0ebe5d786a705
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBAgAGBQJUQbA0AAoJEIm7uGY+LmXOcfIP/2KISAfdH5eHoMbR+YEZSPC7
YmhfuitFgf2Q94itF5K33j1QDtzAIFyNQSrAPRZJrf3aKuoaVGMjB0yOJ5pLqagL
kfuHlLVXXCZeRxkGn1dp0KSJCzkpMYWgmCh1sFRkoB/9SJYEAF0pW0GNUGlCWoGR
uqAcwOv+KGv3K2DHDG0vaSa4DFpGPMqAGIF0ZhPXUwrrYw5P1iPBp/WTjhyRVEfd
lEALQ2byIbUK4JC5TSNFpj7hkPHbxMNWlFC6uX+4Y1ZSa+ZJFe3/1UuCiJPVvw9C
7JQRZhWBH2XK3fq8s71ovSLDrkyyEAGkby6pKN0HguJfqAEpWNFi0RQ73rxG/U1+
bPZ4OcXSHyFn9NWYeX60WGTCL6kHus10Il01PAcS1k1j+O4wGajuCXeAiPQtfCU6
D1BjBmI3SNBktkSuEZecOjSy0msbqRRN3B+bMdmQO9oCf0T9YpDQ1JmaRoBrJkUC
f3tSjP/2HhJgWsi+NHgH885G+2N5nXl8J5EZ7OCfX1xi9oMGOYNbGCs6Ovki7UuY
0g4RqTWd24sr+Qor5k3FbaCjjCV98KVTGalyUGcH9wB5D6CzaCmRIXHZB7q7Bq5P
7Mp/6y9ZO2J+iaj3lKboKSgJTrZdhIAyzyfkWqGgbu8HYh3Gbf9pffLeL/8Xyguj
C36EXr+hz3Ecz2Dm1kEo
=pkgg
-----END PGP SIGNATURE-----

@theuni
Copy link
Member

theuni commented Oct 18, 2014

I'm not sure this makes things any clearer. I'll delay judgement until giving it a re-read this weekend.

@TheBlueMatt
Copy link
Contributor

It makes it marginally clearer, and the move to the .cpp is nice.

@sipa
Copy link
Member Author

sipa commented Oct 18, 2014

I should probably clarify a bit why I think this is nicer. Previously, there were some different ways of pushing vectors/numbers:

  • operator<<(vector): pushes the vector using OP_0, direct push, OP_PUSHDATA1 or OP_PUSHDATA2, but not using OP_1..OP_16 or OP_1NEGATE.
  • opterator<<(int64_t) pushes the vector representing the number using OP_1NEGATE, OP_0..OP_16 or a direct push.
  • operator<<(CSCriptNum) pushes the vector representing the number using OP_0 or direct push.

So the logic for number conversion and vector-to-opcode conversion was being done in several places, and pushing a vector or scriptnum would never result in using OP_1..OP_16 or OP_1NEGATE, even if that was the shortest way of doing it (which would violate BIP62).

This pull request moves all number-to-vector logic to CScriptNum, and all vector-to-opcode logic to operator<<(vector), and makes the rest wrappers around it. Also, it removes a bunch of unnecessary methods.

@sipa
Copy link
Member Author

sipa commented Oct 25, 2014

Rebased.

@theuni Do you understand my motivation now for this?

@theuni
Copy link
Member

theuni commented Oct 26, 2014

@sipa Yes, It's fine by me. My iffyness stemmed from the fact that this changes behavior somewhat from "I'll blindly add whatever you tell me to" to "just tell me what you want and I'll clean it up". I'd usually prefer to allow people to shoot themselves in the foot if they're really trying, but this really isn't the place for that.

Fwiw, I really meant for the ScriptBinaryData change (what eventually turned into the ToByteVector() function) to be used for this. I had hoped to leave a method around for adding exact data to a script. That would've made the chainparams change a good bit cleaner. But I suppose it's possible there aren't enough of those cases to justify easy the foot-shooting.

@sipa
Copy link
Member Author

sipa commented Oct 27, 2014

@laanwj comments?

@gavinandresen
Copy link
Contributor

utACK

@sipa
Copy link
Member Author

sipa commented Oct 29, 2014

Rebased.

sipa added 2 commits November 4, 2014 05:55
* Make CScript::operator<<(const std::vector<unsigned char>&) cover all push
  operations (including OP_0, OP_1..OP_16 and OP_1NEGATE), and make the push
  for numbers use that.
* Convert the uint256, uint160 and CPubKey into a single templated one using
  the vector push code.
* Get rid of the int64_t operator<<; better be explicit about what needs pushing,
  so change all number pushes to use CScriptNum (which, due to the generic
  vector push now also correctly uses OP_n).
* Get rid of some unused methods and constructors as a result.
@sipa
Copy link
Member Author

sipa commented Nov 4, 2014

Rebased.

@jtimon
Copy link
Contributor

jtimon commented Nov 5, 2014

Untested ACK

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@petertodd I think this would actually be a consensus change...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep! Well, maybe, probably not. I think this may only matter in the case of a valid signature, so you'd need a valid signature that is representable by a non-push opcode.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TheBlueMatt If STRICTENC was written such that it caused the script to immediately fail, this could matter as CHECKMULTISIG evaluation exits early; the fork would be the difference between a passing CHECKMULTISIG NOT and a failed script.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, though I think its rather orthogonal to this issue...and we knew (its in the src!) that STRICTENC was not a softfork.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not the point. It's an example of how a very subtle change could have lead to a fork because of a known consensus-critical dependency. Just because you don't know how to exploit it right now doesn't mean you can't.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh? My point was that STRICTENC is already a hardfork, and while I agree 100% with everything you said in your last statement, its again not connected to the previous discussion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, you're comment gives the impression that you're arguing this change is ok, on the basis that it can't be exploited right now. I'm arguing this change isn't ok, because we have solid evidence that it changes behavior that could be used for an exploit. I'm not sure how, and maybe it isn't possible, but it's obviously a risky change.

Adding a separate method to serialize a unsigned char vector to a PUSHDATA and nothing else, and changing FindAndDelete() calls to use that method, may be one way to mitigate the risk.

Note how all of the above also suggests that conversions of data to CScripts() is something that should ideally be kept separate of the consensus-critical code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also a good argument for incorporating hashed instruction-by-instruction execution traces into any future OP_EVAL proposal, where failure to match the trace hash exactly fails immediately. Merkelized abstract syntax tree ideas lend themselves naturally to this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point was not that this is OK, but that arguing based on STRICTENC is a completely invalid argument. In any case, we clearly agree, so...whatever.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's nothing invalid about arguing that we had a close call. We should follow development practices that prevent close calls, given it's only a matter of time before one of those close calls turns into an actual exploit.

@sipa
Copy link
Member Author

sipa commented Nov 6, 2014

Too risky. Closing.

@sipa sipa closed this Nov 6, 2014
@jtimon
Copy link
Contributor

jtimon commented Nov 7, 2014

Maybe we can push a subset of the changes that is not risky?

@bitcoin bitcoin locked as resolved and limited conversation to collaborators Sep 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants