diff --git a/internal/staging/stdscript/script.go b/internal/staging/stdscript/script.go index 4f0a5c70..970e8e71 100644 --- a/internal/staging/stdscript/script.go +++ b/internal/staging/stdscript/script.go @@ -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 diff --git a/internal/staging/stdscript/script_test.go b/internal/staging/stdscript/script_test.go index 503549fe..964fce19 100644 --- a/internal/staging/stdscript/script_test.go +++ b/internal/staging/stdscript/script_test.go @@ -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 diff --git a/internal/staging/stdscript/scriptv0.go b/internal/staging/stdscript/scriptv0.go index d04a15a9..71d65e10 100644 --- a/internal/staging/stdscript/scriptv0.go +++ b/internal/staging/stdscript/scriptv0.go @@ -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: + // + + // 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 + 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 diff --git a/internal/staging/stdscript/scriptv0_test.go b/internal/staging/stdscript/scriptv0_test.go index 92aa1ebd..ba96595b 100644 --- a/internal/staging/stdscript/scriptv0_test.go +++ b/internal/staging/stdscript/scriptv0_test.go @@ -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 + } + } +}