This breaks the dependency on chaincfg.SigHashOptimization which is no
longer available in v2 of the chaincfg module. The constant is set to
false to ensure the same semantics are kept and an additional comment
has been added regarding the status.
This modifies the CalcSignatureHash function to make use of the new
signature hash calculation function that accepts raw scripts without
needing to first parse them. Consequently, it also doubles as a slight
optimization to the execution time and a significant reduction in the
number of allocations.
In order to convert the CalcScriptHash function and keep the same
semantics, a new function named checkScriptParses is introduced which
will quickly determine if a script can be fully parsed without failure
and return the parse failure in the case it can't.
The following is a before and after comparison of analyzing a large
multiple input transaction:
benchmark old ns/op new ns/op delta
-------------------------------------------------------
BenchmarkCalcSigHash 2792057 2760042 -1.15%
benchmark old allocs new allocs delta
-------------------------------------------------------
BenchmarkCalcSigHash 1691 1068 -36.84%
benchmark old bytes new bytes delta
-------------------------------------------------------
BenchmarkCalcSigHash 521673 438604 -15.92%
This introduces a new function named calcSignatureHashRaw which accepts
the raw script bytes to calculate the script hash versus requiring the
parsed opcode only to unparse them later in order to make it more
flexible for working with raw scripts.
Since there are several places in the rest of the code that currently
only have access to the parsed opcodes, this modifies the existing
calcSignatureHash to first unparse the script before calling the new
function.
Note that the code in the signature hash calculation to remove all
instances of OP_CODESEPARATOR from the script is removed because that is
a holdover from BTC code which does not apply to v0 Decred scripts since
OP_CODESEPARATOR is completely disabled in Decred and thus there can
never actually be one in the script.
Finally, it removes the removeOpcode function and related tests since it
is no longer used.
This converts the majority of script errors from generic errors created
via errors.New and fmt.Errorf to use a concrete type that implements the
error interface with an error code and description.
This allows callers to programmatically detect the type of error via
type assertions and an error code while still allowing the errors to
provide more context.
For example, instead of just having an error the reads "disabled opcode"
as would happen prior to these changes when a disabled opcode is
encountered, the error will now read "attempt to execute disabled opcode
OP_FOO".
While it was previously possible to programmatically detect many errors
due to them being exported, they provided no additional context and
there were also various instances that were just returning errors
created on the spot which callers could not reliably detect without
resorting to looking at the actual error message, which is nearly always
bad practice.
Also, while here, export the MaxStackSize and MaxScriptSize constants
since they can be useful for consumers of the package and perform some
minor cleanup of some of the tests.
This removes the SigHashOld definition from the txscript package since
it both has never been used in Decred and it has also always been
invalid to use due to the fact that strict encoding has always been
active and required by consensus in Decred.
This modifies the signature hash calculation logic to completely
decouple it from the wire transaction serialization and significantly
optimizes it in the process. It also significantly improves the
comments to specifically call out the semantics.
This change is highly desirable because it has several beneficial
properties:
- The signature hash semantics are much clearer and specific
- There is no longer a need to copy the entire transaction and modify
the relevant portions since the necessary substitutions are made on
the fly
- Allows much faster calculation by serializing directly into byte
slices and avoiding all of the additional error handling logic
dedicated to handling network streams
- Provides the possibility of changing the wire format without breaking
signature hash calculation
- Note that the caching portion still relies on the wire format, but
that can be addressed in future commits since it is merely an
optimization that can also be decoupled
The following is a before and after comparison of signature hash
calculation for both speed and memory allocations for a transaction with
many inputs:
benchmark old ns/op new ns/op delta
------------------------------------------------------------
BenchmarkCalcSigHash 6299714 1551740 -75.37%
benchmark old allocs new allocs delta
------------------------------------------------------------
BenchmarkCalcSigHash 18601 1691 -90.91%
This removes the SigHashAllValue signature hash type. This is being
done because it is not currently usable without a consensus change due
to a consensus rule which enforces strict signature encoding disallowing
the hash type.
While it would be possible to change the consensus rule in question to
include SigHashAllValue, that would obviously require a consensus vote
since it constitutes a change to the consensus rules. Given that a vote
is required to make any changes in regards to this, it is ideal to
completely change the algorithm altogether to not only address this
issue, but also to address other shortcomings in regards to efficiency
and complexity of the current algorithm in addition to committing to all
input amounts per the aforementioned description.
This moves the logic to calculate the signature hash and the associated
test to separate files. Since there will ultimately be a new signature
algorithm, it makes sense to separate all logic related to signature
hashes for better code organization.
It contains no functional changes.