diff --git a/internal/staging/stdscript/script.go b/internal/staging/stdscript/script.go index 970e8e71..b5b493b5 100644 --- a/internal/staging/stdscript/script.go +++ b/internal/staging/stdscript/script.go @@ -99,6 +99,14 @@ const ( // imposes. The script is commonly referred to as a redeem script. STStakeSubmissionScriptHash + // STStakeGenPubKeyHash identifies a script that is only valid when used as + // part of a vote transaction in the staking system. + // + // It imposes an encumbrance that requires a secp256k1 public key that + // hashes to a specific value along with a valid ECDSA signature for that + // public key. + STStakeGenPubKeyHash + // numScriptTypes is the maximum script type number used in tests. This // entry MUST be the last entry in the enum. numScriptTypes @@ -119,6 +127,7 @@ var scriptTypeToName = []string{ STNullData: "nulldata", STStakeSubmissionPubKeyHash: "stakesubmission-pubkeyhash", STStakeSubmissionScriptHash: "stakesubmission-scripthash", + STStakeGenPubKeyHash: "stakegen-pubkeyhash", } // String returns the ScriptType as a human-readable name. @@ -304,6 +313,20 @@ func IsStakeSubmissionScriptHashScript(scriptVersion uint16, script []byte) bool return false } +// IsStakeGenPubKeyHashScript returns whether or not the passed script is a +// standard stake generation pay-to-pubkey-hash script. +// +// NOTE: Version 0 scripts are the only currently supported version. It will +// always return false for other script versions. +func IsStakeGenPubKeyHashScript(scriptVersion uint16, script []byte) bool { + switch scriptVersion { + case 0: + return IsStakeGenPubKeyHashScriptV0(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 964fce19..b5192908 100644 --- a/internal/staging/stdscript/script_test.go +++ b/internal/staging/stdscript/script_test.go @@ -28,6 +28,7 @@ func TestScriptTypeStringer(t *testing.T) { {STNullData, "nulldata"}, {STStakeSubmissionPubKeyHash, "stakesubmission-pubkeyhash"}, {STStakeSubmissionScriptHash, "stakesubmission-scripthash"}, + {STStakeGenPubKeyHash, "stakegen-pubkeyhash"}, {0xff, "invalid"}, } @@ -147,6 +148,7 @@ func TestDetermineScriptType(t *testing.T) { testIsX(IsNullDataScript, STNullData) testIsX(IsStakeSubmissionPubKeyHashScript, STStakeSubmissionPubKeyHash) testIsX(IsStakeSubmissionScriptHashScript, STStakeSubmissionScriptHash) + testIsX(IsStakeGenPubKeyHashScript, STStakeGenPubKeyHash) // 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 71d65e10..7f75f7f0 100644 --- a/internal/staging/stdscript/scriptv0.go +++ b/internal/staging/stdscript/scriptv0.go @@ -494,6 +494,22 @@ func IsStakeSubmissionScriptHashScriptV0(script []byte) bool { return ExtractStakeSubmissionScriptHashV0(script) != nil } +// ExtractStakeGenPubKeyHashV0 extracts the public key hash from the +// passed script if it is a standard version 0 stake generation +// pay-to-pubkey-hash script. It will return nil otherwise. +func ExtractStakeGenPubKeyHashV0(script []byte) []byte { + // A stake generation pay-to-pubkey-hash script is of the form: + // OP_SSGEN + const stakeOpcode = txscript.OP_SSGEN + return extractStakePubKeyHashV0(script, stakeOpcode) +} + +// IsStakeGenPubKeyHashScriptV0 returns whether or not the passed script is a +// standard version 0 stake generation pay-to-pubkey-hash script. +func IsStakeGenPubKeyHashScriptV0(script []byte) bool { + return ExtractStakeGenPubKeyHashV0(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. @@ -523,6 +539,8 @@ func DetermineScriptTypeV0(script []byte) ScriptType { return STStakeSubmissionPubKeyHash case IsStakeSubmissionScriptHashScriptV0(script): return STStakeSubmissionScriptHash + case IsStakeGenPubKeyHashScriptV0(script): + return STStakeGenPubKeyHash } return STNonStandard diff --git a/internal/staging/stdscript/scriptv0_test.go b/internal/staging/stdscript/scriptv0_test.go index ba96595b..0ced379c 100644 --- a/internal/staging/stdscript/scriptv0_test.go +++ b/internal/staging/stdscript/scriptv0_test.go @@ -555,6 +555,25 @@ var scriptV0Tests = func() []scriptTest { script: p("SSTX HASH160 DATA_20 0x%s EQUAL", p2sh), wantType: STStakeSubmissionScriptHash, wantData: hexToBytes(p2sh), + }, { + // --------------------------------------------------------------------- + // Negative stake submission generation P2PKH tests. + // --------------------------------------------------------------------- + + name: "almost v0 stake gen p2pkh-ecdsa-secp256k1 -- wrong hash length", + script: p("SSGEN DUP HASH160 DATA_21 0x00%s EQUALVERIFY CHECKSIG", + h160CE), + wantType: STNonStandard, + }, { + // --------------------------------------------------------------------- + // Positive stake submission generation P2PKH tests. + // --------------------------------------------------------------------- + + name: "v0 stake gen p2pkh-ecdsa-secp256k1", + script: p("SSGEN DUP HASH160 DATA_20 0x%s EQUALVERIFY CHECKSIG", + h160CE), + wantType: STStakeGenPubKeyHash, + wantData: hexToBytes(h160CE), }} }() @@ -829,3 +848,24 @@ func TestExtractStakeSubmissionScriptHashV0(t *testing.T) { } } } + +// TestExtractStakeGenPubKeyHashV0 ensures that extracting a public key +// hash from a version 0 stake generation pay-to-pubkey-hash script works as +// intended for all of the version 0 test scripts. +func TestExtractStakeGenPubKeyHashV0(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 == STStakeGenPubKeyHash { + want = asByteSlice(t, test) + } + + got := ExtractStakeGenPubKeyHashV0(test.script) + if !bytes.Equal(got, want) { + t.Errorf("%q: unexpected pubkey hash -- got %x, want %x", test.name, + got, want) + continue + } + } +}