neo3/neo_builder/transaction/
invocation_script.rs

1use std::hash::Hash;
2
3use crate::{
4	builder::{BuilderError, ScriptBuilder},
5	codec::{Decoder, Encoder, NeoSerializable},
6	crypto::{KeyPair, Secp256r1Signature},
7	var_size, OpCode,
8};
9use getset::{Getters, Setters};
10use serde_derive::{Deserialize, Serialize};
11
12/// An invocation script is part of a witness and is simply a sequence of neo-vm instructions.
13///
14/// The invocation script usually is the input to the verification script.
15///
16/// In most cases it will contain a signature that is checked in the verification script.
17#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters, Setters, Serialize, Deserialize)]
18pub struct InvocationScript {
19	/// This invocation script as a byte array
20	#[getset(get = "pub", set = "pub")]
21	script: Vec<u8>,
22}
23
24impl InvocationScript {
25	/// Constructs an empty invocation script.
26	pub fn new() -> Self {
27		Self { script: Vec::new() }
28	}
29
30	/// Creates an invocation script with the given script.
31	///
32	/// It is recommended to use `InvocationScript::from_signature` or `InvocationScript::from_message_and_key_pair`
33	/// when you need a signature invocation script.
34	///
35	/// # Arguments
36	///
37	/// * `script` - The script as a byte array
38	pub fn new_with_script(script: Vec<u8>) -> Self {
39		Self { script }
40	}
41
42	pub fn from_serialized_script(script: Vec<u8>) -> Self {
43		let mut decoder = Decoder::new(&script);
44		Self::decode(&mut decoder).unwrap()
45	}
46
47	/// Creates an invocation script from the given signature.
48	///
49	/// # Arguments
50	///
51	/// * `signature` - The signature to use in the script
52	///
53	/// # Returns
54	///
55	/// The constructed invocation script
56	pub fn from_signature(signature: Secp256r1Signature) -> Self {
57		let mut script = ScriptBuilder::new();
58		let signature_bytes = signature.to_bytes();
59		script.push_data(signature_bytes.to_vec());
60		Self { script: script.to_bytes() }
61	}
62
63	/// Creates an invocation script from the signature of the given message signed with the given key pair.
64	///
65	/// # Arguments
66	///
67	/// * `message` - The message to sign
68	/// * `key_pair` - The key to use for signing
69	///
70	/// # Returns
71	///
72	/// The constructed invocation script
73	pub fn from_message_and_key_pair(
74		message: Vec<u8>,
75		key_pair: &KeyPair,
76	) -> Result<Self, BuilderError> {
77		let signature = key_pair.private_key.sign_tx(&message)?;
78		Ok(Self::from_signature(signature))
79	}
80
81	/// Constructs an invocation script from the given signatures.
82	///
83	/// # Arguments
84	///
85	/// * `signatures` - The signatures
86	///
87	/// # Returns
88	///
89	/// The invocation script
90	pub fn from_signatures(signatures: &[Secp256r1Signature]) -> Self {
91		let mut builder = ScriptBuilder::new();
92		for signature in signatures {
93			let signature_bytes = signature.to_bytes();
94			builder.push_data(signature_bytes.to_vec());
95		}
96		Self { script: builder.to_bytes() }
97	}
98}
99
100impl InvocationScript {
101	/// Unbundles the script into a list of signatures if this invocation script contains signatures.
102	///
103	/// # Returns
104	///
105	/// The list of signatures found in this script
106	pub fn get_signatures(&self) -> Vec<Secp256r1Signature> {
107		let mut reader = Decoder::new(&self.script);
108		let mut sigs = Vec::new();
109		while reader.available() > 0 && reader.read_u8() == OpCode::PushData1 as u8 {
110			reader.read_u8(); // ignore opcode size
111			if let Ok(signature) = Secp256r1Signature::from_bytes(&reader.read_bytes(64).unwrap()) {
112				sigs.push(signature);
113			}
114		}
115		sigs
116	}
117}
118
119impl NeoSerializable for InvocationScript {
120	type Error = BuilderError;
121
122	fn size(&self) -> usize {
123		return var_size(self.script.len()) + self.script.len();
124	}
125
126	fn encode(&self, writer: &mut Encoder) {
127		writer.write_var_bytes(&self.script);
128	}
129
130	fn decode(reader: &mut Decoder) -> Result<Self, Self::Error> {
131		let script = reader.read_var_bytes()?;
132		Ok(Self { script })
133	}
134	fn to_array(&self) -> Vec<u8> {
135		let mut writer = Encoder::new();
136		self.encode(&mut writer);
137		writer.to_bytes()
138	}
139}
140
141#[cfg(test)]
142mod tests {
143	use crate::neo_crypto::utils::ToHexString;
144
145	use super::*;
146
147	#[test]
148	fn test_from_message_and_key_pair() {
149		let message = vec![0u8; 10];
150		let key_pair = KeyPair::new_random();
151		let script =
152			InvocationScript::from_message_and_key_pair(message.clone(), &key_pair).unwrap();
153		let expected_signature = key_pair.private_key().sign_tx(&message).unwrap();
154		let expected = format!(
155			"{}40{}",
156			OpCode::PushData1.to_hex_string(),
157			hex::encode(expected_signature.to_bytes())
158		);
159		assert_eq!(hex::decode(&expected).unwrap(), script.script);
160		assert_eq!(hex::decode(&format!("42{}", expected)).unwrap(), script.to_array());
161	}
162
163	#[test]
164	fn test_serialize_random_invocation_script() {
165		let message = vec![1; 10];
166		let script = InvocationScript::new_with_script(message.clone());
167		assert_eq!(message, script.script);
168	}
169
170	#[test]
171	fn test_deserialize_custom_invocation_script() {
172		let message = vec![1; 256];
173		let script = format!("{}0001{}", OpCode::PushData2.to_hex_string(), hex::encode(&message));
174		let serialized_script = format!("FD0301{}", script);
175		let deserialized =
176			InvocationScript::from_serialized_script(hex::decode(&serialized_script).unwrap());
177		assert_eq!(deserialized.script, hex::decode(&script).unwrap());
178	}
179
180	#[test]
181	fn test_deserialize_signature_invocation_script() {
182		let message = vec![0u8; 10];
183		let key_pair = KeyPair::new_random();
184		let signature = key_pair.private_key().sign_tx(&message).unwrap();
185		let script =
186			format!("{}40{}", OpCode::PushData1.to_hex_string(), hex::encode(signature.to_bytes()));
187		let deserialized = InvocationScript::from_serialized_script(
188			hex::decode(&format!("42{}", script)).unwrap(),
189		);
190		assert_eq!(deserialized.script, hex::decode(&script).unwrap());
191	}
192
193	#[test]
194	fn test_size() {
195		let script = hex::decode("147e5f3c929dd830d961626551dbea6b70e4b2837ed2fe9089eed2072ab3a655523ae0fa8711eee4769f1913b180b9b3410bbb2cf770f529c85f6886f22cbaaf").unwrap();
196		let s = InvocationScript::new_with_script(script);
197		assert_eq!(s.size(), 65);
198	}
199
200	#[test]
201	fn test_get_signatures() {
202		let message = vec![0u8; 10];
203		let key_pair = KeyPair::new_random();
204		let signature = key_pair.private_key.sign_tx(&message).unwrap();
205		let inv = InvocationScript::from_signatures(&vec![
206			signature.clone(),
207			signature.clone(),
208			signature.clone(),
209		]);
210		inv.get_signatures().iter().for_each(|sig| assert_eq!(*sig, signature));
211	}
212}