Skip to content

Conversation

Kixunil
Copy link
Collaborator

@Kixunil Kixunil commented Feb 20, 2025

This implements various improvements related to Script. Please refer to the individual commits for details.

This is a part of #4059

The newtype sanity rules (a name I came up with):
* Newtypes should have at most one constructor that directly references
  the inner field.
* Newtypes should have at most three accessor methods that directly
  reference the ineer field: one for owned access, the second for
  borrowed and the third for mutably borrowed.
* All other methods should use the methods above to perform operations
  on the newtype and not directly access the fields.

This commit makes `Script` and `ScriptBuf` obey these except for
`reserve` and `reserve_exact` since we don't have `as_mut_vec` method.
As a side effect it also adds `const` to `ScriptBuf::from_bytes`.
We're confident these methods will never perform syscalls, so it's fine
to add `const` to them.
We have several trait implementations for `Script` and `ScriptBuf` in a
common module so that it's easy to verify that they are same but `Deref`
and `DerefMut` should *not* be implemented for `Script` so having them
in the common module is not helpful. This moves them to the appropriate
`Owned` module.
The `Arbitrary` trait allows zero-copy implementations for borrowed
types, so this adds the implementation for borrowed `Script`.
These methods are either newtype casts that should compile to no-ops or
directly calling into some other function with at most some pointer
adjustments. As such making them `#[inline]` is definitely benefitial.
There are also methods that check length and then call some other
function. These are also worth inlining since the length could be known
at compile time and the check could be eliminated.
These comments said that the modules should be private but they already
are. Also, the internals of the newtypes became private a few commits
ago.
There are already several methods referring to capacity but none to
retrieve it. Those methods also promise certain behavior that mandates
having *a* capacity field inside the struct, so no changes in layout
will ever remove it. Thus it's OK to expose the field.

Aside from exposing the field, this also fixes up the tests to obey the
sanity rules.
The Electrum protocol uses hashes of `script_pubkey` that might look
similar to the ones we have in the crate and could be confused. This
change notes that to hopefully avoid the confusion.

Resolves rust-bitcoin#3997
These were re-implementing hashing after the check rather than calling
the `_unchecked` method, so this replaces the manual implementation with
the method.
@coveralls
Copy link

coveralls commented Feb 20, 2025

Pull Request Test Coverage Report for Build 13442187736

Details

  • 33 of 43 (76.74%) changed or added relevant lines in 3 files are covered.
  • 5 unchanged lines in 2 files lost coverage.
  • Overall coverage decreased (-0.01%) to 82.72%

Changes Missing Coverage Covered Lines Changed/Added Lines %
primitives/src/script/owned.rs 14 15 93.33%
primitives/src/script/borrowed.rs 13 16 81.25%
primitives/src/script/mod.rs 6 12 50.0%
Files with Coverage Reduction New Missed Lines %
primitives/src/script/borrowed.rs 1 92.5%
primitives/src/script/mod.rs 4 75.9%
Totals Coverage Status
Change from base Build 13420890001: -0.01%
Covered Lines: 21183
Relevant Lines: 25608

💛 - Coveralls

Using `Self` instead of specific type name can make some refactorings
easier.
@Kixunil Kixunil force-pushed the script-improvements branch from cd7cda8 to 5680b4e Compare February 20, 2025 18:30
Copy link
Member

@tcharding tcharding left a comment

Choose a reason for hiding this comment

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

ACK 5680b4e

Comment on lines +38 to +39
/// Note: there is another "script hash" object in bitcoin ecosystem (Electrum protocol) that
/// uses 256-bit hash and hashes a semantically different script. Thus, this type cannot
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// Note: there is another "script hash" object in bitcoin ecosystem (Electrum protocol) that
/// uses 256-bit hash and hashes a semantically different script. Thus, this type cannot
/// Note: there is another "script hash" object in the bitcoin ecosystem (Electrum protocol)
/// that uses 256-bit hash and hashes a semantically different script. Thus, this type cannot

Adds 'the' in front of 'bitcoin eccosystem and maintains 100 column width.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I suggest we do these later unless I have to push. This PR blocks script tagging and while I don't have it ready yet, I'd like to parallelize our work.

Copy link
Member

Choose a reason for hiding this comment

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

Sweet as.

Comment on lines +45 to +46
/// Note: there is another "script hash" object in bitcoin ecosystem (Electrum protocol) that
/// looks similar to this one also being SHA256, however, they hash semantically different
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// Note: there is another "script hash" object in bitcoin ecosystem (Electrum protocol) that
/// looks similar to this one also being SHA256, however, they hash semantically different
/// Note: there is another "script hash" object in the bitcoin ecosystem (Electrum protocol)
/// that looks similar to this one also being SHA256, however, they hash semantically different

@tcharding
Copy link
Member

This is a sexy PR.


/// Returns the number of **bytes** available for writing without reallocation.
///
/// It is guaranteed that `script.capacity() >= script.len()` always holds.
Copy link
Contributor

Choose a reason for hiding this comment

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

is script.capacity() >= script.len() an invariant here?

Copy link
Collaborator Author

@Kixunil Kixunil Feb 22, 2025

Choose a reason for hiding this comment

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

Of course, same as on Vec.

I did briefly think about supporting capacity = 0 for 'static references but that has its downsides and scripts should never be static except for the empty one in script_sig which doesn't allocate anyway. (Also we could still do it by pretending that capacity == len whenever it's 0.)

Copy link
Member

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

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

ACK 5680b4e; successfully ran local tests

@apoelstra apoelstra merged commit 6c286e3 into rust-bitcoin:master Feb 24, 2025
24 checks passed
@Kixunil Kixunil deleted the script-improvements branch February 25, 2025 09:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants