neo3/neo_clients/
utils.rs

1use std::{future::Future, pin::Pin, str::FromStr, sync::Arc, time::Duration};
2
3use crate::{
4	builder::VerificationScript,
5	config::DEFAULT_ADDRESS_VERSION,
6	crypto::{private_key_to_public_key, HashableForVec, Secp256r1PrivateKey, Secp256r1PublicKey},
7	neo_clients::ProviderError,
8	ScriptHash, ScriptHashExtension,
9};
10use futures_timer::Delay;
11use futures_util::{stream, FutureExt, StreamExt};
12use primitive_types::{H160, U256};
13use regex::Regex;
14use wiremock::{Match, Request};
15
16/// A simple gas escalation policy
17pub type EscalationPolicy = Box<dyn Fn(U256, usize) -> U256 + Send + Sync>;
18
19// Helper type alias
20#[cfg(target_arch = "wasm32")]
21pub(crate) type PinBoxFut<'a, T> = Pin<Box<dyn Future<Output = Result<T, ProviderError>> + 'a>>;
22#[cfg(not(target_arch = "wasm32"))]
23pub(crate) type PinBoxFut<'a, T> =
24	Pin<Box<dyn Future<Output = Result<T, ProviderError>> + Send + 'a>>;
25
26/// Calls the future if `item` is None, otherwise returns a `futures::ok`
27pub async fn maybe<F, T, E>(item: Option<T>, f: F) -> Result<T, E>
28where
29	F: Future<Output = Result<T, E>>,
30{
31	if let Some(item) = item {
32		futures_util::future::ok(item).await
33	} else {
34		f.await
35	}
36}
37
38/// Create a stream that emits items at a fixed interval. Used for rate control
39pub fn interval(duration: Duration) -> impl futures_core::stream::Stream<Item = ()> + Send + Unpin {
40	stream::unfold((), move |_| Delay::new(duration).map(|_| Some(((), ())))).map(drop)
41}
42
43// A generic function to serialize any data structure that implements Serialize trait
44pub fn serialize<T: serde::Serialize>(t: &T) -> serde_json::Value {
45	serde_json::to_value(t).expect("Failed to serialize value")
46}
47
48/// Convert a script to a script hash.
49pub fn script_hash_from_script(script: &[u8]) -> ScriptHash {
50	let hash = script.sha256_ripemd160();
51	H160::from_slice(&hash)
52}
53
54/// Convert a public key to an address.
55pub fn public_key_to_address(public_key: &Secp256r1PublicKey) -> String {
56	let script_hash = public_key_to_script_hash(public_key);
57	script_hash_to_address(&script_hash)
58}
59
60/// Convert a public key to a script hash.
61pub fn public_key_to_script_hash(public_key: &Secp256r1PublicKey) -> ScriptHash {
62	let script = VerificationScript::from_public_key(public_key);
63	ScriptHash::from_script(&script.script())
64}
65
66/// Convert a private key to a script hash.
67pub fn private_key_to_script_hash(private_key: &Secp256r1PrivateKey) -> ScriptHash {
68	let pubkey = private_key_to_public_key(private_key);
69	public_key_to_script_hash(&pubkey)
70}
71
72/// Convert a private key to an address.
73pub fn private_key_to_address(private_key: &Secp256r1PrivateKey) -> String {
74	let script_hash = private_key_to_script_hash(private_key);
75	script_hash_to_address(&script_hash)
76}
77
78/// Convert a script hash to an address.
79pub fn script_hash_to_address(script_hash: &ScriptHash) -> String {
80	let mut data = vec![DEFAULT_ADDRESS_VERSION];
81	let mut script_hash_bytes = script_hash.clone().as_bytes().to_vec();
82	script_hash_bytes.reverse();
83	data.extend_from_slice(&script_hash_bytes);
84	let sha = &data.hash256().hash256();
85	data.extend_from_slice(&sha[..4]);
86	bs58::encode(data).into_string()
87}
88
89/// Convert an address to a script hash.
90pub fn address_to_script_hash(address: &str) -> Result<ScriptHash, ProviderError> {
91	let bytes = match bs58::decode(address).into_vec() {
92		Ok(bytes) => bytes,
93		Err(_) => return Err(ProviderError::InvalidAddress),
94	};
95	let _salt = bytes[0];
96	let hash = &bytes[1..21];
97	let checksum = &bytes[21..25];
98	let sha = &bytes[..21].hash256().hash256();
99	let check = &sha[..4];
100	if checksum != check {
101		return Err(ProviderError::InvalidAddress);
102	}
103
104	let mut rev = [0u8; 20];
105	rev.clone_from_slice(hash);
106	rev.reverse();
107	Ok(H160::from(&rev))
108}
109
110/// Convert a script hash to hex format.
111pub fn script_hash_to_hex(script_hash: &ScriptHash) -> String {
112	let bytes: [u8; 20] = script_hash.to_fixed_bytes();
113	hex::hex::encode(bytes)
114}
115
116/// Convert a script hash in hex format to a ScriptHash.
117pub fn script_hash_from_hex(hex: &str) -> Result<ScriptHash, ProviderError> {
118	H160::from_str(hex).map_err(|_| ProviderError::InvalidAddress)
119}
120
121/// Convert an address to hex format.
122pub fn address_to_hex(address: &str) -> Result<String, ProviderError> {
123	let script_hash = H160::from_address(address)?;
124	Ok(hex::hex::encode(script_hash.to_fixed_bytes()))
125}
126
127/// Convert a hex format script hash to an address.
128pub fn hex_to_address(hex: &str) -> Result<String, ProviderError> {
129	let script_hash = H160::from_str(hex).map_err(|_| ProviderError::InvalidAddress)?;
130	Ok(script_hash.to_address())
131}
132
133pub struct BodyRegexMatcher {
134	pattern: Arc<Regex>,
135}
136
137impl BodyRegexMatcher {
138	pub fn new(pattern: &str) -> Self {
139		BodyRegexMatcher { pattern: Arc::new(Regex::new(pattern).expect("Invalid regex pattern")) }
140	}
141}
142
143impl Match for BodyRegexMatcher {
144	fn matches(&self, request: &Request) -> bool {
145		std::str::from_utf8(&request.body)
146			.map(|body_str| self.pattern.is_match(body_str))
147			.unwrap_or(false)
148	}
149}