stdscript: Add v0 stake sub p2sh support.

This adds support for detecting version 0 stake submission
pay-to-script-hash scripts as standard and extracting the associated
script hash.

Full test coverage is included.

This is part of a series of commits to fully implement the stdscript
package.
This commit is contained in:
Dave Collins 2021-05-30 02:55:40 -05:00
parent 3ea8aa854b
commit 02bcf8414f
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
4 changed files with 99 additions and 0 deletions

View File

@ -90,6 +90,15 @@ const (
// public key.
STStakeSubmissionPubKeyHash
// STStakeSubmissionScriptHash identifies a script that is only valid when
// used as part of a ticket purchase transaction in the staking system and
// is used for imposing voting rights.
//
// It imposes an encumbrance that requires a script that hashes to a
// specific value along with all of the encumbrances that script itself
// imposes. The script is commonly referred to as a redeem script.
STStakeSubmissionScriptHash
// numScriptTypes is the maximum script type number used in tests. This
// entry MUST be the last entry in the enum.
numScriptTypes
@ -109,6 +118,7 @@ var scriptTypeToName = []string{
STMultiSig: "multisig",
STNullData: "nulldata",
STStakeSubmissionPubKeyHash: "stakesubmission-pubkeyhash",
STStakeSubmissionScriptHash: "stakesubmission-scripthash",
}
// String returns the ScriptType as a human-readable name.
@ -280,6 +290,20 @@ func IsStakeSubmissionPubKeyHashScript(scriptVersion uint16, script []byte) bool
return false
}
// IsStakeSubmissionScriptHashScript returns whether or not the passed script is
// a standard stake submission pay-to-script-hash script.
//
// NOTE: Version 0 scripts are the only currently supported version. It will
// always return false for other script versions.
func IsStakeSubmissionScriptHashScript(scriptVersion uint16, script []byte) bool {
switch scriptVersion {
case 0:
return IsStakeSubmissionScriptHashScriptV0(script)
}
return false
}
// DetermineScriptType returns the type of the script passed.
//
// NOTE: Version 0 scripts are the only currently supported version. It will

View File

@ -27,6 +27,7 @@ func TestScriptTypeStringer(t *testing.T) {
{STMultiSig, "multisig"},
{STNullData, "nulldata"},
{STStakeSubmissionPubKeyHash, "stakesubmission-pubkeyhash"},
{STStakeSubmissionScriptHash, "stakesubmission-scripthash"},
{0xff, "invalid"},
}
@ -145,6 +146,7 @@ func TestDetermineScriptType(t *testing.T) {
testIsX(IsMultiSigScript, STMultiSig)
testIsX(IsNullDataScript, STNullData)
testIsX(IsStakeSubmissionPubKeyHashScript, STStakeSubmissionPubKeyHash)
testIsX(IsStakeSubmissionScriptHashScript, STStakeSubmissionScriptHash)
// Ensure the special case of determining if a signature script appears
// to be a signature script which consists of a pay-to-script-hash

View File

@ -461,6 +461,39 @@ func IsStakeSubmissionPubKeyHashScriptV0(script []byte) bool {
return ExtractStakeSubmissionPubKeyHashV0(script) != nil
}
// extractStakeScriptHashV0 extracts the script hash from the passed script if
// it is a standard version 0 stake-tagged pay-to-script-hash script with the
// provided stake opcode. It will return nil otherwise.
func extractStakeScriptHashV0(script []byte, stakeOpcode byte) []byte {
// A stake-tagged pay-to-script-hash is of the form:
// <stake opcode> <standard-pay-to-script-hash script>
// The script can't possibly be a stake-tagged pay-to-script-hash if it
// doesn't start with the given stake opcode. Fail fast to avoid more work
// below.
if len(script) < 1 || script[0] != stakeOpcode {
return nil
}
return txscript.ExtractScriptHash(script[1:])
}
// ExtractStakeSubmissionScriptHashV0 extracts the script hash from the
// passed script if it is a standard version 0 stake submission
// pay-to-script-hash script. It will return nil otherwise.
func ExtractStakeSubmissionScriptHashV0(script []byte) []byte {
// A stake submission pay-to-script-hash script is of the form:
// OP_SSTX <standard-pay-to-script-hash script>
const stakeOpcode = txscript.OP_SSTX
return extractStakeScriptHashV0(script, stakeOpcode)
}
// IsStakeSubmissionScriptHashScriptV0 returns whether or not the passed script
// is a standard version 0 stake submission pay-to-script-hash script.
func IsStakeSubmissionScriptHashScriptV0(script []byte) bool {
return ExtractStakeSubmissionScriptHashV0(script) != nil
}
// DetermineScriptTypeV0 returns the type of the passed version 0 script from
// the known standard types. This includes both types that are required by
// consensus as well as those which are not.
@ -488,6 +521,8 @@ func DetermineScriptTypeV0(script []byte) ScriptType {
return STNullData
case IsStakeSubmissionPubKeyHashScriptV0(script):
return STStakeSubmissionPubKeyHash
case IsStakeSubmissionScriptHashScriptV0(script):
return STStakeSubmissionScriptHash
}
return STNonStandard

View File

@ -538,6 +538,23 @@ var scriptV0Tests = func() []scriptTest {
script: p("SSTX DUP HASH160 DATA_20 0x%s EQUALVERIFY CHECKSIG", h160CE),
wantType: STStakeSubmissionPubKeyHash,
wantData: hexToBytes(h160CE),
}, {
// ---------------------------------------------------------------------
// Negative stake submission P2SH tests.
// ---------------------------------------------------------------------
name: "almost v0 stake submission p2sh -- wrong hash length",
script: p("SSTX HASH160 DATA_21 0x00%s EQUAL", p2sh),
wantType: STNonStandard,
}, {
// ---------------------------------------------------------------------
// Positive stake submission P2SH tests.
// ---------------------------------------------------------------------
name: "v0 stake submission p2sh",
script: p("SSTX HASH160 DATA_20 0x%s EQUAL", p2sh),
wantType: STStakeSubmissionScriptHash,
wantData: hexToBytes(p2sh),
}}
}()
@ -791,3 +808,24 @@ func TestExtractStakeSubmissionPubKeyHashV0(t *testing.T) {
}
}
}
// TestExtractStakeSubmissionScriptHashV0 ensures that extracting a script hash
// from a version 0 stake submission pay-to-script-hash script works as intended
// for all of the version 0 test scripts.
func TestExtractStakeSubmissionScriptHashV0(t *testing.T) {
for _, test := range scriptV0Tests {
// Determine the expected data based on the expected script type and
// data specified in the test.
var want []byte
if test.wantType == STStakeSubmissionScriptHash {
want = asByteSlice(t, test)
}
got := ExtractStakeSubmissionScriptHashV0(test.script)
if !bytes.Equal(got, want) {
t.Errorf("%q: unexpected script hash -- got %x, want %x", test.name,
got, want)
continue
}
}
}