neo3/neo_clients/
utils.rs1use 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
16pub type EscalationPolicy = Box<dyn Fn(U256, usize) -> U256 + Send + Sync>;
18
19#[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
26pub 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
38pub 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
43pub fn serialize<T: serde::Serialize>(t: &T) -> serde_json::Value {
45 serde_json::to_value(t).expect("Failed to serialize value")
46}
47
48pub fn script_hash_from_script(script: &[u8]) -> ScriptHash {
50 let hash = script.sha256_ripemd160();
51 H160::from_slice(&hash)
52}
53
54pub 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
60pub 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
66pub 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
72pub 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
78pub 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
89pub 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
110pub 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
116pub fn script_hash_from_hex(hex: &str) -> Result<ScriptHash, ProviderError> {
118 H160::from_str(hex).map_err(|_| ProviderError::InvalidAddress)
119}
120
121pub 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
127pub 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}