aurora_engine_sdk/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2// All `as` conversions in this code base have been carefully reviewed and are safe.
3#![allow(clippy::as_conversions)]
4
5use crate::prelude::{Address, H256, STORAGE_PRICE_PER_BYTE};
6#[cfg(feature = "contract")]
7use crate::prelude::{Vec, U256};
8pub use types::keccak;
9
10pub mod base64;
11pub mod caching;
12pub mod env;
13pub mod error;
14pub mod io;
15#[cfg(feature = "contract")]
16pub mod near_runtime;
17mod prelude;
18pub mod promise;
19pub mod types;
20
21#[cfg(feature = "contract")]
22use near_runtime::exports;
23
24#[cfg(feature = "contract")]
25const ECRECOVER_MESSAGE_SIZE: u64 = 32;
26#[cfg(feature = "contract")]
27const ECRECOVER_SIGNATURE_LENGTH: u64 = 64;
28#[cfg(feature = "contract")]
29const ECRECOVER_MALLEABILITY_FLAG: u64 = 0;
30
31#[cfg(feature = "contract")]
32pub fn panic_utf8(bytes: &[u8]) -> ! {
33    unsafe {
34        exports::panic_utf8(bytes.len() as u64, bytes.as_ptr() as u64);
35    }
36    unreachable!()
37}
38
39#[cfg(feature = "contract")]
40pub fn log_utf8(bytes: &[u8]) {
41    unsafe {
42        exports::log_utf8(bytes.len() as u64, bytes.as_ptr() as u64);
43    }
44}
45
46/// Calls environment sha256 on given input.
47#[cfg(feature = "contract")]
48#[must_use]
49pub fn sha256(input: &[u8]) -> H256 {
50    unsafe {
51        const REGISTER_ID: u64 = 1;
52        exports::sha256(input.len() as u64, input.as_ptr() as u64, 1);
53        let bytes = H256::zero();
54        exports::read_register(REGISTER_ID, bytes.0.as_ptr() as u64);
55        bytes
56    }
57}
58
59#[cfg(not(feature = "contract"))]
60#[must_use]
61pub fn sha256(input: &[u8]) -> H256 {
62    use sha2::Digest;
63
64    let output = sha2::Sha256::digest(input);
65    H256(output.into())
66}
67
68/// Calls environment ripemd160 on given input.
69#[cfg(feature = "contract")]
70#[must_use]
71pub fn ripemd160(input: &[u8]) -> [u8; 20] {
72    unsafe {
73        const REGISTER_ID: u64 = 1;
74        exports::ripemd160(input.len() as u64, input.as_ptr() as u64, REGISTER_ID);
75        let bytes = [0u8; 20];
76        exports::read_register(REGISTER_ID, bytes.as_ptr() as u64);
77        bytes
78    }
79}
80
81#[cfg(feature = "contract")]
82#[must_use]
83pub fn alt_bn128_g1_sum(left: [u8; 64], right: [u8; 64]) -> [u8; 64] {
84    let mut bytes = Vec::with_capacity(64 * 2 + 2); // 64 bytes per G1 + 2 positive integer bytes.
85
86    bytes.push(0); // positive sign
87    bytes.extend_from_slice(&left);
88    bytes.push(0);
89    bytes.extend_from_slice(&right);
90
91    let value_ptr = bytes.as_ptr() as u64;
92    let value_len = bytes.len() as u64;
93
94    unsafe {
95        const REGISTER_ID: u64 = 1;
96        exports::alt_bn128_g1_sum(value_len, value_ptr, REGISTER_ID);
97        let mut output = [0u8; 64];
98        exports::read_register(REGISTER_ID, output.as_ptr() as u64);
99        let x = U256::from_little_endian(&output[0..32]);
100        let y = U256::from_little_endian(&output[32..64]);
101        output[0..32].copy_from_slice(&x.to_big_endian());
102        output[32..64].copy_from_slice(&y.to_big_endian());
103        output
104    }
105}
106
107#[cfg(feature = "contract")]
108#[must_use]
109pub fn alt_bn128_g1_scalar_multiple(g1: [u8; 64], fr: [u8; 32]) -> [u8; 64] {
110    let mut bytes = [0u8; 96];
111    bytes[0..64].copy_from_slice(&g1);
112    bytes[64..96].copy_from_slice(&fr);
113
114    let value_ptr = bytes.as_ptr() as u64;
115    let value_len = bytes.len() as u64;
116
117    unsafe {
118        const REGISTER_ID: u64 = 1;
119        exports::alt_bn128_g1_multiexp(value_len, value_ptr, REGISTER_ID);
120        let mut output = [0u8; 64];
121        exports::read_register(REGISTER_ID, output.as_ptr() as u64);
122        let x = U256::from_little_endian(&output[0..32]);
123        let y = U256::from_little_endian(&output[32..64]);
124        output[0..32].copy_from_slice(&x.to_big_endian());
125        output[32..64].copy_from_slice(&y.to_big_endian());
126        output
127    }
128}
129
130#[cfg(feature = "contract")]
131pub fn alt_bn128_pairing<I>(pairs: I) -> bool
132where
133    I: ExactSizeIterator<Item = ([u8; 64], [u8; 128])>,
134{
135    let n = pairs.len();
136    let mut bytes = Vec::with_capacity(n * 6 * 32);
137    let mut buf = [0u8; 64 + 128];
138    for (g1, g2) in pairs {
139        buf[0..64].copy_from_slice(&g1);
140        buf[64..192].copy_from_slice(&g2);
141        bytes.extend_from_slice(&buf);
142    }
143
144    let value_ptr = bytes.as_ptr() as u64;
145    let value_len = bytes.len() as u64;
146
147    let result = unsafe { exports::alt_bn128_pairing_check(value_len, value_ptr) };
148
149    result == 1
150}
151
152/// Recover address from message hash and signature.
153#[cfg(feature = "contract")]
154pub fn ecrecover(hash: H256, signature: &[u8]) -> Result<Address, ECRecoverErr> {
155    unsafe {
156        const RECOVER_REGISTER_ID: u64 = 1;
157        const KECCACK_REGISTER_ID: u64 = 2;
158
159        let hash_ptr = hash.as_ptr() as u64;
160        let sig_ptr = signature.as_ptr() as u64;
161        let result = exports::ecrecover(
162            ECRECOVER_MESSAGE_SIZE,
163            hash_ptr,
164            ECRECOVER_SIGNATURE_LENGTH,
165            sig_ptr,
166            u64::from(signature[64]),
167            ECRECOVER_MALLEABILITY_FLAG,
168            RECOVER_REGISTER_ID,
169        );
170        if result == u64::from(true) {
171            // The result from the ecrecover call is in a register; we can use this
172            // register directly for the input to keccak256. This is why the length is
173            // set to `u64::MAX`.
174            exports::keccak256(u64::MAX, RECOVER_REGISTER_ID, KECCACK_REGISTER_ID);
175            let keccak_hash_bytes = [0u8; 32];
176            exports::read_register(KECCACK_REGISTER_ID, keccak_hash_bytes.as_ptr() as u64);
177            Ok(Address::try_from_slice(&keccak_hash_bytes[12..]).map_err(|_| ECRecoverErr)?)
178        } else {
179            Err(ECRecoverErr)
180        }
181    }
182}
183
184#[cfg(not(feature = "contract"))]
185pub fn ecrecover(hash: H256, signature: &[u8]) -> Result<Address, ECRecoverErr> {
186    use sha3::Digest;
187
188    let hash = libsecp256k1::Message::parse_slice(hash.as_bytes()).map_err(|_| ECRecoverErr)?;
189    let v = signature[64];
190    let signature = libsecp256k1::Signature::parse_standard_slice(&signature[0..64])
191        .map_err(|_| ECRecoverErr)?;
192    let bit = match v {
193        0..=26 => v,
194        _ => v - 27,
195    };
196    let recovery_id = libsecp256k1::RecoveryId::parse(bit).map_err(|_| ECRecoverErr)?;
197
198    libsecp256k1::recover(&hash, &signature, &recovery_id)
199        .map_err(|_| ECRecoverErr)
200        .and_then(|public_key| {
201            // recover returns a 65-byte key, but addresses come from the raw 64-byte key
202            let r = sha3::Keccak256::digest(&public_key.serialize()[1..]);
203            Address::try_from_slice(&r[12..]).map_err(|_| ECRecoverErr)
204        })
205}
206
207#[cfg(feature = "contract")]
208pub fn log(data: &str) {
209    log_utf8(data.as_bytes());
210}
211
212#[cfg(not(feature = "contract"))]
213#[allow(clippy::missing_const_for_fn)]
214pub fn log(_data: &str) {
215    // TODO: standalone logging
216}
217
218#[macro_export]
219macro_rules! log {
220    ($($args:tt)*) => {
221        #[cfg(feature = "log")]
222        $crate::log(&aurora_engine_types::format!("{}", format_args!($($args)*)))
223    };
224}
225
226#[must_use]
227pub const fn storage_byte_cost() -> u128 {
228    STORAGE_PRICE_PER_BYTE
229}
230
231pub struct ECRecoverErr;
232
233impl ECRecoverErr {
234    #[must_use]
235    pub const fn as_str(&self) -> &'static str {
236        "ERR_ECRECOVER"
237    }
238}
239
240impl AsRef<[u8]> for ECRecoverErr {
241    fn as_ref(&self) -> &[u8] {
242        self.as_str().as_bytes()
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249    use aurora_engine_types::types::Address;
250    use aurora_engine_types::H256;
251
252    const SIGNATURE_LENGTH: usize = 65;
253
254    fn ecverify(hash: H256, signature: &[u8], signer: Address) -> bool {
255        matches!(ecrecover(hash, signature[0..SIGNATURE_LENGTH].try_into().unwrap()), Ok(s) if s == signer)
256    }
257
258    #[test]
259    fn test_ecverify() {
260        let hash = H256::from_slice(
261            &hex::decode("1111111111111111111111111111111111111111111111111111111111111111")
262                .unwrap(),
263        );
264        let signature =
265            &hex::decode("b9f0bb08640d3c1c00761cdd0121209268f6fd3816bc98b9e6f3cc77bf82b69812ac7a61788a0fdc0e19180f14c945a8e1088a27d92a74dce81c0981fb6447441b")
266                .unwrap();
267        let signer = Address::try_from_slice(
268            &hex::decode("1563915e194D8CfBA1943570603F7606A3115508").unwrap(),
269        )
270        .unwrap();
271        assert!(ecverify(hash, signature, signer));
272    }
273}