Tests: enforce critical class instance attributes in functional tests, fix segwit test specificity #14305

pull JustinTArthur wants to merge 5 commits into bitcoin:master from JustinTArthur:functional-test-attr-enforcement changing 4 files +153 −50
  1. JustinTArthur commented at 3:00 am on September 24, 2018: contributor

    No extra attributes will be able to be added to instances of the C++ class ports or of other critical classes without causing an exception. Helps prevent adding or depending on attributes that aren’t in the intended object structure. It may prevent issues such as the one fixed in bitcoin/bitcoin#14300.

    This request fixes the erroneous version attribute used in the p2p_segwit.py functional tests. This pull includes the commit from bitcoin/bitcoin#14300.

  2. test: Fix broken segwit test ba923e32a0
  3. fanquake added the label Tests on Sep 24, 2018
  4. JustinTArthur commented at 3:15 am on September 24, 2018: contributor
    Travis tests should be re-run following the merge of #14300. Rebased this PR on #14300, so dependency isn’t an issue.
  5. Fix for incorrect version attr set on functional test segwit block. 1d0ce94a54
  6. JustinTArthur force-pushed on Sep 24, 2018
  7. DrahtBot commented at 4:13 am on September 24, 2018: member
    • #14318 (Relay ancestors of transactions by sdaftuar)
    • #14312 (tests: Remove unused testing code by practicalswift)
    • #14220 (Transaction relay privacy bugfix by sdaftuar)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  8. JustinTArthur renamed this:
    Functional test attribute enforcement
    Tests: enforce critical class instance attributes in functional tests
    on Sep 24, 2018
  9. practicalswift commented at 5:49 am on September 24, 2018: contributor

    What an excellent first-time contribution! Thanks!

    This is my favourite type of contribution: it not only fixes current code but also fixes future code by introducing a mechanism that helps us avoid introducing new instances of the same class of issue. Exactly the type of contribution that I was referring to in #14217 (comment):

    Personally I’m much more excited to see a PR submitted which strengthens the long term security of the project in some form compared to when I see a PR submitted which implements some shiny new feature :-)

    In addition to the above: __slots__ is also an unambiguous improvement from a memory usage perspective.

    utACK 965c8ff9c7539719eb36bd46297358e33b571766

  10. jnewbery commented at 12:23 pm on September 24, 2018: member

    Concept ACK.

    There’s some suggestion in this thread: https://stackoverflow.com/questions/472000/usage-of-slots that using __slots__ to constrain attribute creation is discouraged. However, I’ve searched for a few minutes and haven’t been able to find a ‘better’ way to do it.

  11. in test/functional/p2p_segwit.py:772 in 965c8ff9c7 outdated
    770@@ -771,8 +771,8 @@ def test_p2sh_witness(self):
    771         # segwit-aware would also reject this for failing CLEANSTACK.
    772         test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False)
    


    MarcoFalke commented at 12:47 pm on September 24, 2018:

    Would be nice to demonstrate that this fails due to the correct reason: “non-mandatory-script-verify-flag (Witness program was passed an empty witness)”

    You can achieve this with the assert_debug_log helper.


    JustinTArthur commented at 2:11 am on September 26, 2018:
    I agree. Thanks for the tip on assert_debug_log. I’m more familiar with Python than I am of the test suite. Addressed in a new commit.
  12. in test/functional/p2p_segwit.py:777 in 965c8ff9c7 outdated
    770@@ -771,8 +771,8 @@ def test_p2sh_witness(self):
    771         # segwit-aware would also reject this for failing CLEANSTACK.
    772         test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False)
    773 
    774-        # Try to put the witness script in the script_sig, should also fail.
    775-        spend_tx.vin[0].script_sig = CScript([p2wsh_pubkey, b'a'])
    776+        # Try to put the witness script in the scriptSig, should also fail.
    777+        spend_tx.vin[0].scriptSig = CScript([p2wsh_pubkey, b'a'])
    778         spend_tx.rehash()
    779         test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False)
    


    MarcoFalke commented at 12:49 pm on September 24, 2018:
    Same here: The reject reason is just the same as above, whereas it now should be “mandatory-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)”

    JustinTArthur commented at 2:11 am on September 26, 2018:
    Addressed in a new commit.
  13. MarcoFalke commented at 12:53 pm on September 24, 2018: member

    Concept ACK. I guess this could also be implemented with a “frozen” decorator, which freezes the attributes after __init__.

    It worries me that the tests just pass with or without this fix, which doesn’t add much value to the fix. Would you mind changing the tests itself, such that they would fail without, but pass with the change. (See inline comments)

  14. gmaxwell commented at 0:25 am on September 26, 2018: contributor
    You are my hero of the day. I am python ignorant, and so I’m only concept ACKing because (1) I’ve run into this problem before and been frustrated, (2) I see people who know the tests better haven’t opposed it, and (3) I asked for someone to fix this. :P
  15. JustinTArthur commented at 2:28 am on September 26, 2018: contributor

    Concept ACK. I guess this could also be implemented with a “frozen” decorator, which freezes the attributes after __init__.

    I’m not opposed to moving to that at some point. Using __slots__ everywhere makes the code more verbose, where Python can otherwise excel at being concise. Unfortunately, these tests seem to do a bit of assignment after initialization so some refactoring would be necessary.

    Added some checks for specific transaction acceptance failures in the debug log per @MarcoFalke’s suggestion. Now at least the scriptSig typo would have caused a failure.

    Thanks for the feedback, @MarcoFalke @gmaxwell and @practicalswift.

  16. JustinTArthur commented at 5:27 am on September 26, 2018: contributor

    @MarcoFalke I’ve created an alternate implementation that uses a freezing decorator as you’ve suggested: https://github.com/bitcoin/bitcoin/compare/master...JustinTArthur:functional-test-attr-enforcement-decorator Decorator implementation isn’t great to look at, but makes the classes easier on the eyes. We’d lose the performance benefits of __slots__, if that matters. Unlike the freezing method used by attrs and Python 3.7 data classes, this will allow re-assignment of initialized attributes.

    If the new implementation is preferred across the board, I can replace this PR’s commits with the alternate ones. For what it’s worth, I prefer __slots__. I don’t think it’s an aspect of the language at risk of breaking in the Python 3.x timeframe and while using double-underscore stuff never looks great, I don’t see __slots__ as being much different from supplying an __init__. We’re starting to see __slots__ mentioned in more of the higher level Python docs.

  17. JustinTArthur renamed this:
    Tests: enforce critical class instance attributes in functional tests
    Tests: enforce critical class instance attributes in functional tests, fix segwit test specificity
    on Sep 26, 2018
  18. ken2812221 commented at 6:54 am on September 26, 2018: contributor
    Concept ACK. Will try to learn how __slots__ work.
  19. promag commented at 8:43 am on September 26, 2018: member
    Concept ACK, +1 __slots__ for this case.
  20. in test/functional/p2p_segwit.py:778 in 35c9a46e69 outdated
    777-        spend_tx.vin[0].script_sig = CScript([p2wsh_pubkey, b'a'])
    778+        # Try to put the witness script in the scriptSig, should also fail.
    779+        spend_tx.vin[0].scriptSig = CScript([p2wsh_pubkey, b'a'])
    780         spend_tx.rehash()
    781-        test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False)
    782+        with self.nodes[0].assert_debug_log(expected_msgs=('Not relaying invalid transaction {}'.format(spend_tx.hash), 'mandatory-script-verify-flag-failed')):
    


    MarcoFalke commented at 3:17 pm on September 26, 2018:

    nit: Could use the full error message here?

    'was not accepted: mandatory-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)'


    JustinTArthur commented at 2:44 am on September 27, 2018:
    Reasonable, will adjust commit.
  21. in test/functional/p2p_segwit.py:772 in 35c9a46e69 outdated
    768@@ -769,12 +769,14 @@ def test_p2sh_witness(self):
    769         # will require a witness to spend a witness program regardless of
    770         # segwit activation.  Note that older bitcoind's that are not
    771         # segwit-aware would also reject this for failing CLEANSTACK.
    772-        test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False)
    773+        with self.nodes[0].assert_debug_log(expected_msgs=(spend_tx.hash, 'was not accepted: non-mandatory-script-verify-flag')):
    


    MarcoFalke commented at 3:17 pm on September 26, 2018:

    nit: Could use the full error message here?

    'was not accepted: non-mandatory-script-verify-flag (Witness program was passed an empty witness)'


    JustinTArthur commented at 2:44 am on September 27, 2018:
    Reasonable, will adjust commit.
  22. MarcoFalke approved
  23. MarcoFalke commented at 3:21 pm on September 26, 2018: member

    I slightly prefer the decorator for its brevity when used and thus easy reusability. We don’t care about performance improvements if they are not significant or can not be measured.

    Though, if slots are the commonly the preferred thing, I am fine with that.

  24. jnewbery commented at 3:42 pm on September 26, 2018: member
    Concept ACK either approach. I slightly prefer the __slots__ approach, since it seems more explicit to me and doesn’t require us to roll our own attribute-fixing code, but I’m happy with either.
  25. MarcoFalke commented at 4:01 pm on September 26, 2018: member
    Ok, let’s keep the slots then.
  26. practicalswift commented at 4:49 pm on September 26, 2018: contributor

    Agree with @jnewbery – the explicitness of __slots__ is nice. Also: it is more idiomatic since it works out of the box without any extra code.

    From the Zen of Python:

    • Explicit is better than implicit.
    • There should be one – and preferably only one – obvious way to do it.

    :-)

  27. in test/functional/test_framework/script.py:371 in 35c9a46e69 outdated
    367+class CScriptNum:
    368+    __slots__ = ("value",)
    369+
    370     def __init__(self, d=0):
    371         self.value = d
    372 
    


    jimmysong commented at 11:52 pm on September 26, 2018:
    Any reason for not doing __slots__ = () for CScript below?

    JustinTArthur commented at 2:36 am on September 27, 2018:

    I avoided it because it used to be documented as impossible for variable length sequences. Looks like recent revisions have been updated to clarify that it’s ok as long as it’s an empty __slots__.

    I’ll revise, test-to-confirm, and adjust the commit.


    JustinTArthur commented at 3:17 am on September 27, 2018:
    Done
  28. jimmysong commented at 11:57 pm on September 26, 2018: contributor

    ACK

    Nice use of the slots feature! I would like to see something instructing new devs in the Python testing documentation (functional/README.md) so people don’t trip over it.

  29. Strictly enforce instance attrs in critical functional test classes.
    Additionally, removed redundant parentheses and added PEP-8 compliant
    spacing around those classes.
    3a4449e9ad
  30. Check for specific tx acceptance failures based on script signature 17b42f4122
  31. Document fixed attribute behavior in critical test framework classes.
    Per @jimmysong's suggestion in bitcoin/bitcoin#14305. Also corrects
    module for network objects and wrappers.
    e460232876
  32. JustinTArthur force-pushed on Sep 27, 2018
  33. JustinTArthur commented at 3:19 am on September 27, 2018: contributor
    Revised commits to incorporate code comment suggestions from @MarcoFalke and @jimmysong.
  34. jimmysong approved
  35. jimmysong commented at 10:53 am on September 27, 2018: contributor

    Thanks for the changes!

    ACK

  36. jnewbery commented at 2:52 pm on September 27, 2018: member
    utACK e46023287689fc8e79b9a82fe1a827d87c769423
  37. MarcoFalke commented at 3:10 pm on September 27, 2018: member
    utACK e460232876
  38. MarcoFalke merged this on Sep 27, 2018
  39. MarcoFalke closed this on Sep 27, 2018

  40. MarcoFalke referenced this in commit edcf29c9da on Sep 27, 2018
  41. JustinTArthur deleted the branch on Sep 28, 2018
  42. Bushstar referenced this in commit 627988a8ea on Oct 9, 2018
  43. jamesob referenced this in commit 8d4ac6bb9a on Oct 16, 2018
  44. jfhk referenced this in commit fd014461c2 on Nov 14, 2018
  45. HashUnlimited referenced this in commit 0a3efe19d8 on Nov 26, 2018
  46. pravblockc referenced this in commit 56a1b672aa on Jul 28, 2021
  47. pravblockc referenced this in commit f94903d288 on Jul 30, 2021
  48. pravblockc referenced this in commit 63b069cd49 on Aug 2, 2021
  49. pravblockc referenced this in commit 6b131e6b39 on Aug 4, 2021
  50. pravblockc referenced this in commit f7b1f7c2d7 on Aug 4, 2021
  51. pravblockc referenced this in commit 1d3f047cd1 on Aug 4, 2021
  52. DrahtBot locked this on Sep 8, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-12-19 06:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me