1use crate::{
2 builder::{BuilderError, CallFlags, InteropService},
3 codec::Encoder,
4 crypto::Secp256r1PublicKey,
5 neo_crypto::utils::{FromBase64String, FromHexString, ToHexString},
6 Bytes, ContractParameter, ContractParameterType, OpCode, ParameterValue, ScriptHashExtension,
7};
8use futures_util::future::ok;
9use getset::{Getters, Setters};
10use num_bigint::BigInt;
11use num_traits::{Signed, ToPrimitive};
12use primitive_types::H160;
13use serde::{Deserialize, Serialize};
14use std::{
15 cmp::PartialEq,
16 collections::HashMap,
17 convert::TryInto,
18 fmt::{Debug, Formatter},
19 str::FromStr,
20};
21use tokio::io::AsyncWriteExt;
22
23#[derive(Debug, PartialEq, Eq, Hash, Getters, Setters)]
44pub struct ScriptBuilder {
45 #[getset(get = "pub")]
46 pub script: Encoder,
47}
48
49impl ScriptBuilder {
50 pub fn new() -> Self {
60 Self { script: Encoder::new() }
61 }
62
63 pub fn op_code(&mut self, op_codes: &[OpCode]) -> &mut Self {
83 for opcode in op_codes {
84 self.script.write_u8(opcode.opcode());
85 }
86 self
87 }
88
89 pub fn op_code_with_arg(&mut self, opcode: OpCode, argument: Bytes) -> &mut Self {
110 self.script.write_u8(opcode.opcode());
111 let _ = self.script.write_bytes(&argument);
112 self
113 }
114
115 pub fn contract_call(
147 &mut self,
148 hash160: &H160,
149 method: &str,
150 params: &[ContractParameter],
151 call_flags: Option<CallFlags>,
152 ) -> Result<&mut Self, BuilderError> {
153 if params.is_empty() {
154 self.op_code(&[OpCode::NewArray0]);
155 } else {
156 self.push_params(params);
157 }
158
159 Ok(self
160 .push_integer(BigInt::from(match call_flags {
161 Some(flags) => flags.value(),
162 None => CallFlags::All.value(),
163 }))
164 .push_data(method.as_bytes().to_vec())
165 .push_data(hash160.to_vec())
166 .sys_call(InteropService::SystemContractCall))
167 }
168
169 pub fn sys_call(&mut self, operation: InteropService) -> &mut Self {
189 self.push_opcode_bytes(
190 OpCode::Syscall,
191 operation
192 .hash()
193 .from_hex_string()
194 .map_err(|e| {
195 BuilderError::IllegalArgument(format!("Invalid operation hash: {}", e))
196 })
197 .expect("InteropService hash should always be valid hex"),
198 )
199 }
200
201 pub fn push_params(&mut self, params: &[ContractParameter]) -> Result<&mut Self, BuilderError> {
225 for param in params {
226 self.push_param(param).map_err(|e| {
227 BuilderError::IllegalArgument(format!("Failed to push parameter: {}", e))
228 })?;
229 }
230
231 Ok(self.push_integer(BigInt::from(params.len())).op_code(&[OpCode::Pack]))
232 }
233
234 pub fn push_param(&mut self, param: &ContractParameter) -> Result<&mut Self, BuilderError> {
255 if param.get_type() == ContractParameterType::Any {
256 self.op_code(&[OpCode::PushNull]);
257 return Ok(self);
258 }
259 match ¶m
260 .value
261 .clone()
262 .ok_or_else(|| BuilderError::IllegalArgument("Parameter value is None".to_string()))?
263 {
264 ParameterValue::Boolean(b) => self.push_bool(*b),
265 ParameterValue::Integer(i) => self.push_integer(BigInt::from(i.clone())),
266 ParameterValue::ByteArray(b) => {
267 let bytes = b.from_base64_string().map_err(|e| {
269 BuilderError::IllegalArgument(format!(
270 "Failed to decode base64 ByteArray: {}",
271 e
272 ))
273 })?;
274 self.push_data(bytes)
275 },
276 ParameterValue::Signature(b) | ParameterValue::PublicKey(b) => {
277 self.push_data(b.as_bytes().to_vec())
278 },
279 ParameterValue::H160(h) => self.push_data(h.as_bytes().to_vec()),
280 ParameterValue::H256(h) => self.push_data(h.as_bytes().to_vec()),
281 ParameterValue::String(s) => self.push_data(s.as_bytes().to_vec()),
282 ParameterValue::Array(arr) => self.push_array(arr).map_err(|e| {
283 BuilderError::IllegalArgument(format!("Failed to push array: {}", e))
284 })?,
285 ParameterValue::Map(map) => self
286 .push_map(&map.0)
287 .map_err(|e| BuilderError::IllegalArgument(format!("Failed to push map: {}", e)))?,
288 _ => {
289 return Err(BuilderError::IllegalArgument("Unsupported parameter type".to_string()))
290 },
291 };
292
293 Ok(self)
294 }
295
296 pub fn push_integer(&mut self, i: BigInt) -> &mut Self {
320 if i >= BigInt::from(-1) && i <= BigInt::from(16) {
321 self.op_code(
322 vec![OpCode::try_from(i.to_i32().unwrap() as u8 + OpCode::Push0 as u8).unwrap()]
323 .as_slice(),
324 );
325 } else {
326 let mut bytes = i.to_signed_bytes_le();
327
328 while bytes.len() > 1 && bytes[bytes.len() - 1] == 0 && !i.is_negative() {
334 bytes.pop();
335 }
336
337 let len = bytes.len();
338
339 match len {
342 1 => self.push_opcode_bytes(OpCode::PushInt8, bytes),
343 2 => self.push_opcode_bytes(OpCode::PushInt16, bytes),
344 len if len <= 4 => self.push_opcode_bytes(
345 OpCode::PushInt32,
346 Self::pad_right(&bytes, 4, i.is_negative()),
347 ),
348 len if len <= 8 => self.push_opcode_bytes(
349 OpCode::PushInt64,
350 Self::pad_right(&bytes, 8, i.is_negative()),
351 ),
352 len if len <= 16 => self.push_opcode_bytes(
353 OpCode::PushInt128,
354 Self::pad_right(&bytes, 16, i.is_negative()),
355 ),
356 len if len <= 32 => self.push_opcode_bytes(
357 OpCode::PushInt256,
358 Self::pad_right(&bytes, 32, i.is_negative()),
359 ),
360 _ => {
361 eprintln!("Warning: Integer too large, truncating to 32 bytes");
364 self.push_opcode_bytes(
365 OpCode::PushInt256,
366 Self::pad_right(&bytes[..32.min(bytes.len())], 32, i.is_negative()),
367 )
368 },
369 };
370 }
371
372 self
373 }
374
375 pub fn push_opcode_bytes(&mut self, opcode: OpCode, argument: Vec<u8>) -> &mut ScriptBuilder {
396 self.script.write_u8(opcode as u8);
397 self.script.write_bytes(&argument);
398
399 self
400 }
401
402 fn pad_right(bytes: &[u8], size: usize, negative: bool) -> Vec<u8> {
403 let pad_value = if negative { 0xFF } else { 0 };
404
405 let mut padded = vec![0; size];
406 padded[0..bytes.len()].copy_from_slice(bytes);
407 padded[bytes.len()..].fill(pad_value);
408 padded
409 }
410
411 pub fn push_data(&mut self, data: Vec<u8>) -> &mut Self {
432 match data.len() {
433 0..=0xff => {
434 self.op_code(&[OpCode::PushData1]);
435 self.script.write_u8(data.len() as u8);
436 let _ = self.script.write_bytes(&data);
437 },
438 0x100..=0xffff => {
439 self.op_code(&[OpCode::PushData2]);
440 self.script.write_u16(data.len() as u16);
441 let _ = self.script.write_bytes(&data);
442 },
443 _ => {
444 self.op_code(&[OpCode::PushData4]);
445 self.script.write_u32(data.len() as u32);
446 let _ = self.script.write_bytes(&data);
447 }, }
449 self
450 }
451
452 pub fn push_bool(&mut self, b: bool) -> &mut Self {
471 if b {
472 self.op_code(&[OpCode::PushTrue])
473 } else {
474 self.op_code(&[OpCode::PushFalse])
475 };
476 self
477 }
478
479 pub fn push_array(&mut self, arr: &[ContractParameter]) -> Result<&mut Self, BuilderError> {
491 if arr.is_empty() {
492 self.op_code(&[OpCode::NewArray0]);
493 } else {
494 self.push_params(arr);
495 };
496 Ok(self)
497 }
498
499 pub fn push_map(
510 &mut self,
511 map: &HashMap<ContractParameter, ContractParameter>,
512 ) -> Result<&mut Self, BuilderError> {
513 for (k, v) in map {
514 let kk: ContractParameter = k.clone().into();
515 let vv: ContractParameter = v.clone().into();
516 self.push_param(&vv).unwrap();
517 self.push_param(&kk).unwrap();
518 }
519
520 Ok(self.push_integer(BigInt::from(map.len())).op_code(&[OpCode::PackMap]))
521 }
522
523 pub fn pack(&mut self) -> &mut Self {
529 self.op_code(&[OpCode::Pack])
530 }
531
532 pub fn to_bytes(&self) -> Bytes {
534 self.script.to_bytes()
535 }
536
537 pub fn build_verification_script(pub_key: &Secp256r1PublicKey) -> Bytes {
547 let mut sb = ScriptBuilder::new();
548 sb.push_data(pub_key.get_encoded(true))
549 .sys_call(InteropService::SystemCryptoCheckSig);
550 sb.to_bytes()
551 }
552
553 pub fn build_multi_sig_script(
565 pubkeys: &mut [Secp256r1PublicKey],
566 threshold: u8,
567 ) -> Result<Bytes, BuilderError> {
568 let mut sb = ScriptBuilder::new();
569 sb.push_integer(BigInt::from(threshold));
570 pubkeys.sort_by(|a, b| a.get_encoded(true).cmp(&b.get_encoded(true)));
571 for pk in pubkeys.iter() {
572 sb.push_data(pk.get_encoded(true));
573 }
574 sb.push_integer(BigInt::from(pubkeys.len()));
575 sb.sys_call(InteropService::SystemCryptoCheckMultiSig);
576 Ok(sb.to_bytes())
577 }
578
579 pub fn build_contract_script(
615 sender: &H160,
616 nef_checksum: u32,
617 name: &str,
618 ) -> Result<Bytes, BuilderError> {
619 let mut sb = ScriptBuilder::new();
620 sb.op_code(&[OpCode::Abort])
621 .push_data(sender.to_vec())
622 .push_integer(BigInt::from(nef_checksum))
623 .push_data(name.as_bytes().to_vec());
624 Ok(sb.to_bytes())
625 }
626
627 pub fn build_contract_call_and_unwrap_iterator(
676 contract_hash: &H160,
677 method: &str,
678 params: &[ContractParameter],
679 max_items: u32,
680 call_flags: Option<CallFlags>,
681 ) -> Result<Bytes, BuilderError> {
682 let mut sb = Self::new();
683 sb.push_integer(BigInt::from(max_items));
684
685 sb.contract_call(contract_hash, method, params, call_flags).unwrap();
686
687 sb.op_code(&[OpCode::NewArray]);
688
689 let cycle_start = sb.len();
690 sb.op_code(&[OpCode::Over]);
691 sb.sys_call(InteropService::SystemIteratorNext);
692
693 let jmp_if_not = sb.len();
694 sb.op_code_with_arg(OpCode::JmpIf, vec![0]);
695
696 sb.op_code(&[OpCode::Dup, OpCode::Push2, OpCode::Pick])
697 .sys_call(InteropService::SystemIteratorValue)
698 .op_code(&[
699 OpCode::Append,
700 OpCode::Dup,
701 OpCode::Size,
702 OpCode::Push3,
703 OpCode::Pick,
704 OpCode::Ge,
705 ]);
706
707 let jmp_if_max = sb.len();
708 sb.op_code_with_arg(OpCode::JmpIf, vec![0]);
709
710 let jmp_offset = sb.len();
711 let jmp_bytes = (cycle_start as i32 - jmp_offset as i32) as i8;
713 sb.op_code_with_arg(OpCode::Jmp, vec![jmp_bytes as u8]);
714
715 let load_result = sb.len();
716 sb.op_code(&[OpCode::Nip, OpCode::Nip]);
717
718 let mut script = sb.to_bytes();
719 let jmp_not_bytes = (load_result - jmp_if_not) as i8;
720 script[jmp_if_not + 1] = jmp_not_bytes as u8;
721
722 let jmp_max_bytes = (load_result - jmp_if_max) as i8;
723 script[jmp_if_max + 1] = jmp_max_bytes as u8;
724
725 Ok(script)
726 }
727
728 pub fn len(&self) -> usize {
748 self.script().size()
749 }
750}
751
752#[cfg(test)]
753mod tests {
754 use super::*;
755 use crate::{
756 neo_types::{contract::ContractParameterMap, ContractParameter, ContractParameterType},
757 prelude::Bytes,
758 };
759 use num_bigint::BigInt;
760 use std::collections::HashMap;
761
762 #[test]
763 fn test_push_empty_array() {
764 let mut builder = ScriptBuilder::new();
765 builder.push_array(&[]).unwrap();
766 assert_builder(&builder, &[OpCode::NewArray0 as u8]);
767 }
768
769 #[test]
770 fn test_push_byte_array() {
771 let mut builder = ScriptBuilder::new();
772 let data = vec![0x01, 0x02, 0x03];
773 builder.push_data(data.clone());
774
775 let mut expected = vec![OpCode::PushData1 as u8, data.len() as u8];
776 expected.extend(data);
777 assert_builder(&builder, &expected);
778 }
779
780 #[test]
781 fn test_push_string() {
782 let mut builder = ScriptBuilder::new();
783 let string_data = "Hello, Neo!";
784 builder.push_data(string_data.as_bytes().to_vec());
785
786 let mut expected = vec![OpCode::PushData1 as u8, string_data.len() as u8];
787 expected.extend(string_data.as_bytes());
788 assert_builder(&builder, &expected);
789 }
790
791 #[test]
792 fn test_push_integer() {
793 let mut builder = ScriptBuilder::new();
794
795 builder.push_integer(BigInt::from(0));
797 assert_builder(&builder, &[OpCode::Push0 as u8]);
798
799 let mut builder = ScriptBuilder::new();
800 builder.push_integer(BigInt::from(16));
801 assert_builder(&builder, &[OpCode::Push16 as u8]);
802
803 let mut builder = ScriptBuilder::new();
805 builder.push_integer(BigInt::from(255));
806 assert_builder(&builder, &[OpCode::PushInt8 as u8, 0xff]);
807
808 let mut builder = ScriptBuilder::new();
809 builder.push_integer(BigInt::from(65535));
810 assert_builder(&builder, &[OpCode::PushInt16 as u8, 0xff, 0xff]);
811
812 let mut builder = ScriptBuilder::new();
814 builder.push_integer(BigInt::from(-1000000000000000i64));
815 let expected_bytes = vec![OpCode::PushInt64 as u8, 0, 128, 57, 91, 129, 114, 252, 255];
818 assert_builder(&builder, &expected_bytes);
819
820 let mut builder = ScriptBuilder::new();
821 builder.push_integer(BigInt::from(1000000000000000i64));
822 let pos_big_int = BigInt::from(1000000000000000i64);
825 let pos_bytes = pos_big_int.to_signed_bytes_le();
826 let mut expected_pos = vec![OpCode::PushInt64 as u8];
827 let padded_pos = ScriptBuilder::pad_right(&pos_bytes, 8, false);
828 expected_pos.extend(padded_pos);
829 assert_builder(&builder, &expected_pos);
830 }
831
832 #[test]
833 fn test_verification_script() {
834 let public_key = Secp256r1PublicKey::from_bytes(
835 &hex::decode("035fdb1d1f06759547020891ae97c729327853aeb1256b6fe0473bc2e9fa42ff50")
836 .unwrap(),
837 )
838 .unwrap();
839
840 let script = ScriptBuilder::build_verification_script(&public_key);
841
842 let mut expected = vec![0x0c, 0x21]; expected.extend_from_slice(
846 &hex::decode("035fdb1d1f06759547020891ae97c729327853aeb1256b6fe0473bc2e9fa42ff50")
847 .unwrap(),
848 );
849 expected.push(0x41); expected
851 .extend_from_slice(&hex::decode(&InteropService::SystemCryptoCheckSig.hash()).unwrap());
852
853 assert_eq!(script.to_vec(), expected);
854 }
855
856 #[test]
857 fn test_map() {
858 let mut map = HashMap::new();
859 map.insert(
860 ContractParameter::string("first".to_string()),
861 ContractParameter::byte_array(hex::decode("7365636f6e64").unwrap()),
862 );
863
864 let mut builder = ScriptBuilder::new();
865 builder.push_map(&map).unwrap();
866
867 let expected = builder.to_bytes().to_hex_string();
868
869 let mut builder2 = ScriptBuilder::new();
870 builder2
871 .push_data(hex::decode("7365636f6e64").unwrap())
872 .push_data("first".as_bytes().to_vec())
873 .push_integer(BigInt::from(1))
874 .op_code(&[OpCode::PackMap]);
875
876 let expected2 = builder2.to_bytes().to_hex_string();
877
878 let mut builder3 = ScriptBuilder::new().push_map(&map).unwrap().to_bytes().to_hex_string();
879
880 assert_eq!(expected, expected2);
881 assert_eq!(expected, builder3);
882 }
883
884 #[test]
885 fn test_map_nested() {
886 let mut inner = ContractParameterMap::new();
887 inner.0.insert(
888 ContractParameter::string("inner_key".to_string()),
889 ContractParameter::integer(42),
890 );
891
892 let mut outer = ContractParameterMap::new();
893 outer.0.insert(
894 ContractParameter::string("outer_key".to_string()),
895 ContractParameter::map(inner),
896 );
897
898 let expected = ScriptBuilder::new().push_map(&outer.0).unwrap().to_bytes().to_hex_string();
899
900 let mut manual_builder = ScriptBuilder::new();
902 manual_builder
903 .push_integer(BigInt::from(42))
904 .push_data("inner_key".as_bytes().to_vec())
905 .push_integer(BigInt::from(1))
906 .op_code(&[OpCode::PackMap])
907 .push_data("outer_key".as_bytes().to_vec())
908 .push_integer(BigInt::from(1))
909 .op_code(&[OpCode::PackMap]);
910
911 let manual_expected = manual_builder.to_bytes().to_hex_string();
912
913 assert_eq!(expected, manual_expected);
914 }
915
916 fn assert_builder(builder: &ScriptBuilder, expected: &[u8]) {
917 assert_eq!(builder.to_bytes().to_vec(), expected);
918 }
919
920 fn byte_array(size: usize) -> Vec<u8> {
921 (0..size).map(|i| (i % 256) as u8).collect()
922 }
923}