Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add utility methods for multisig script #30

Merged
merged 2 commits into from
Mar 4, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 93 additions & 27 deletions src/blockdata/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

use std::default::Default;
use std::{error, fmt, io};
use std::str::FromStr;

#[cfg(feature = "serde")] use serde;

Expand Down Expand Up @@ -146,19 +147,13 @@ pub enum MultisigError {
IsNotMultisig,
/// invalid script
InvalidScript,
/// script has an invalid required count
InvalidRequiredSigCount,
/// script has invalid public keys
InvalidPublicKey,
}

impl fmt::Display for MultisigError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let str = match *self {
MultisigError::IsNotMultisig => "script is not multisig",
MultisigError::InvalidScript => "invalid script",
MultisigError::InvalidRequiredSigCount => "script has an invalid required count",
MultisigError::InvalidPublicKey => "script has invalid public keys",
};
f.write_str(str)
}
Expand Down Expand Up @@ -247,6 +242,60 @@ pub enum ColoredCoinError {
UnsuppotedScriptType,
}


/// The types of scripts
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ScriptType {
/// pay-to-pubkey
P2pk,
/// pay-to-pubkey-hash
P2pkh,
/// pay-to-multisig
P2ms,
/// pay-to-script-hash
P2sh,
/// colored pay-to-pubkey-hash
Cp2pkh,
/// colored pay-to-script-hash
Cp2sh,
/// op_return script
Nulldata,
/// non-standard script
NonStandard,
}

impl fmt::Display for ScriptType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
ScriptType::P2pk => "pubkey",
ScriptType::P2pkh => "pubkeyhash",
ScriptType::P2ms => "multisig",
ScriptType::P2sh => "scripthash",
ScriptType::Cp2pkh => "coloredpubkeyhash",
ScriptType::Cp2sh => "coloredscripthash",
ScriptType::Nulldata => "nulldata",
ScriptType::NonStandard => "nonstandard"
})
}
}

impl FromStr for ScriptType {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"pubkey" => Ok(ScriptType::P2pk),
"pubkeyhash" => Ok(ScriptType::P2pkh),
"multisig" => Ok(ScriptType::P2ms),
"scripthash" => Ok(ScriptType::P2sh),
"coloredpubkeyhash" => Ok(ScriptType::Cp2pkh),
"coloredscripthash" => Ok(ScriptType::Cp2sh),
"nulldata" => Ok(ScriptType::Nulldata),
"nonstandard" => Ok(ScriptType::NonStandard),
_ => Err(()),
}
}
}

impl Script {
/// Creates a new empty script
pub fn new() -> Script { Script(vec![].into_boxed_slice()) }
Expand Down Expand Up @@ -422,14 +471,14 @@ impl Script {
if required.is_none() {
required = match op.classify() {
opcodes::Class::PushNum(i) => Some(i),
_ => { return Err(MultisigError::InvalidRequiredSigCount) }
_ => { return Err(MultisigError::IsNotMultisig) }
};
}
}
Instruction::PushBytes(bytes) => {
match PublicKey::from_slice(&bytes) {
Ok(key) => { pubkeys.push(key) },
_ => { return Err(MultisigError::InvalidPublicKey) }
_ => { return Err(MultisigError::IsNotMultisig) }
}
}
}
Expand Down Expand Up @@ -573,24 +622,24 @@ impl Script {
}
}

/// Return type string.
pub fn type_string(&self) -> String {
/// Return script type.
pub fn script_type(&self) -> ScriptType {
if self.is_p2pk() {
"pubkey".to_string()
ScriptType::P2pk
} else if self.is_p2pkh() {
"pubkeyhash".to_string()
ScriptType::P2pkh
} else if self.is_multisig() {
"multisig".to_string()
ScriptType::P2ms
} else if self.is_p2sh() {
"scripthash".to_string()
ScriptType::P2sh
} else if self.is_op_return() {
"nulldata".to_string()
ScriptType::Nulldata
} else if self.is_cp2pkh() {
"coloredpubkeyhash".to_string()
ScriptType::Cp2pkh
} else if self.is_cp2sh() {
"coloredscripthash".to_string()
ScriptType::Cp2sh
} else {
"nonstandard".to_string()
ScriptType::NonStandard
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

enum AddressType is defined in src/util/address.rs and Display trait for string notation is implemented. How about extends it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This method(type_string) is also intended to return script types including non-p2pkh or non-p2sh such as 'OP_RETURN ...'., while AddressType can not be defined for non-standard or op_return script.

Tapyrus-Core implementation: https://github.com/chaintope/tapyrus-core/blob/0a69fa72d394d7cf9119a718f8385f88c5323866/src/script/standard.cpp#L32

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(But we should update AddressType later because AddressType doesnt include colored coin address)

Copy link
Contributor

Choose a reason for hiding this comment

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

for non-standard or op_return script.

Right. Is it a script type rather than an address type?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Add struct ScriptType (cf188c4)

}
Expand Down Expand Up @@ -1408,10 +1457,11 @@ mod test {
];
assert_eq!(multisig.get_multisig_pubkeys(), Ok((2, pubkeys)));

assert_eq!(invalid.get_multisig_pubkeys(), Err(MultisigError::IsNotMultisig));
}

#[test]
fn script_type_string() {
fn script_type() {
let p2pk = hex_script!("2102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397ac");
let p2pkh = hex_script!("76a91446c2fbfbecc99a63148fa076de58cf29b0bcf0b088ac");
let multisig = hex_script!("522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae");
Expand All @@ -1422,21 +1472,37 @@ mod test {
let non_standard = hex_script!("00");

// p2pk
assert_eq!(p2pk.type_string(), "pubkey");
assert_eq!(p2pk.script_type(), ScriptType::P2pk);
assert_eq!(format!("{}", p2pk.script_type()), "pubkey");
assert_eq!(ScriptType::from_str("pubkey"), Ok(ScriptType::P2pk));
// p2pkh
assert_eq!(p2pkh.type_string(), "pubkeyhash");
assert_eq!(p2pkh.script_type(), ScriptType::P2pkh);
assert_eq!(format!("{}", p2pkh.script_type()), "pubkeyhash");
assert_eq!(ScriptType::from_str("pubkeyhash"), Ok(ScriptType::P2pkh));
// multisig
assert_eq!(multisig.type_string(), "multisig");
assert_eq!(multisig.script_type(), ScriptType::P2ms);
assert_eq!(format!("{}", multisig.script_type()), "multisig");
assert_eq!(ScriptType::from_str("multisig"), Ok(ScriptType::P2ms));
// p2sh
assert_eq!(p2sh.type_string(), "scripthash");
assert_eq!(p2sh.script_type(), ScriptType::P2sh);
assert_eq!(format!("{}", p2sh.script_type()), "scripthash");
assert_eq!(ScriptType::from_str("scripthash"), Ok(ScriptType::P2sh));
// op-return
assert_eq!(op_return.type_string(), "nulldata");
assert_eq!(op_return.script_type(), ScriptType::Nulldata);
assert_eq!(format!("{}", op_return.script_type()), "nulldata");
assert_eq!(ScriptType::from_str("nulldata"), Ok(ScriptType::Nulldata));
// cp2pkh
assert_eq!(cp2pkh.type_string(), "coloredpubkeyhash");
assert_eq!(cp2pkh.script_type(), ScriptType::Cp2pkh);
assert_eq!(format!("{}", cp2pkh.script_type()), "coloredpubkeyhash");
assert_eq!(ScriptType::from_str("coloredpubkeyhash"), Ok(ScriptType::Cp2pkh));
// cp2sh
assert_eq!(cp2sh.type_string(), "coloredscripthash");
assert_eq!(cp2sh.script_type(), ScriptType::Cp2sh);
assert_eq!(format!("{}", cp2sh.script_type()), "coloredscripthash");
assert_eq!(ScriptType::from_str("coloredscripthash"), Ok(ScriptType::Cp2sh));
// non-standard
assert_eq!(non_standard.type_string(), "nonstandard");
assert_eq!(non_standard.script_type(), ScriptType::NonStandard);
assert_eq!(format!("{}", non_standard.script_type()), "nonstandard");
assert_eq!(ScriptType::from_str("nonstandard"), Ok(ScriptType::NonStandard));
}

#[test]
Expand Down