Skip to main content

cita_vm/
native.rs

1//! List of precompiled contracts:
2//!
3//!   1. Recovery of ECDSA signature
4//!   2. Hash function SHA256
5//!   3. Hash function RIPEMD160
6//!   4. Identity
7//! Since Byzantium fork
8//!   5. Modular exponentiation
9//!   6. Addition on elliptic curve alt_bn128
10//!   7. Scalar multiplication on elliptic curve alt_bn128
11//!   8. Checking a pairing equation on curve alt_bn128
12use std::io::Write;
13use std::str::FromStr;
14
15use ethereum_types::{Address, H256, H512, U256};
16use ripemd::{Digest, Ripemd160};
17use sha2::Sha256;
18
19use crate::common;
20use crate::err;
21
22/// Implementation of a pre-compiled contract.
23pub trait PrecompiledContract: Send + Sync {
24    /// Return required gas for contract call.
25    fn required_gas(&self, input: &[u8]) -> u64;
26
27    /// Get the output from the pre-compiled contract.
28    fn run(&self, input: &[u8]) -> Result<Vec<u8>, err::Error>;
29}
30
31/// Function get returns a pre-compiled contract by given address.
32pub fn get(address: Address) -> Box<dyn PrecompiledContract> {
33    match U256::from_big_endian(H256::from(address).as_bytes()).low_u64() {
34        0x01 => Box::new(EcRecover {}) as Box<dyn PrecompiledContract>,
35        0x02 => Box::new(SHA256Hash {}) as Box<dyn PrecompiledContract>,
36        0x03 => Box::new(RIPEMD160Hash {}) as Box<dyn PrecompiledContract>,
37        0x04 => Box::new(DataCopy {}) as Box<dyn PrecompiledContract>,
38        0x05 => Box::new(BigModExp {}) as Box<dyn PrecompiledContract>,
39        0x06 => Box::new(Bn256Add {}) as Box<dyn PrecompiledContract>,
40        0x07 => Box::new(Bn256ScalarMul {}) as Box<dyn PrecompiledContract>,
41        0x08 => Box::new(Bn256Pairing {}) as Box<dyn PrecompiledContract>,
42        _ => unimplemented!(),
43    }
44}
45
46/// Check if an address is pre-compiled contract.
47pub fn contains(address: &Address) -> bool {
48    let i = U256::from_big_endian(H256::from(*address).as_bytes());
49    i <= U256::from(8) && !i.is_zero()
50}
51
52const G_ECRECOVER: u64 = 3000; // Elliptic curve sender recovery gas price
53const G_SHA256_BASE: u64 = 60; // Base price for a SHA256 operation
54const G_SHA256_PER_WORD: u64 = 12; // Per-word price for a SHA256 operation
55const G_RIPEMD160_BASE: u64 = 600; // Base price for a RIPEMD160 operation
56const G_RIPEMD160_PER_WORD: u64 = 120; // Per-word price for a RIPEMD160 operation
57const G_IDENTITY_BASE: u64 = 15; // Base price for a data copy operation
58const G_IDENTITY_PER_WORD: u64 = 3; // Per-work price for a data copy operation
59const G_MOD_EXP_QUADCOEFF_DIV: u64 = 20; // Divisor for the quadratic particle of the big int modular exponentiation
60const G_BN256_ADD: u64 = 500; // Gas needed for an elliptic curve addition
61const G_BN256_SCALAR_MUL: u64 = 40000; // Gas needed for an elliptic curve scalar multiplication
62const G_BN256_PARING_BASE: u64 = 100_000; // Base price for an elliptic curve pairing check
63const G_BN256_PARING_PER_POINT: u64 = 80000; // Per-point price for an elliptic curve pairing check
64
65/// Check if each component of the signature is in range.
66fn is_signature_valid(r: &H256, s: &H256, v: u8) -> bool {
67    v <= 1
68        && *r < H256::from_str("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141").unwrap()
69        && *r >= H256::from_low_u64_le(1)
70        && *s < H256::from_str("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141").unwrap()
71        && *s >= H256::from_low_u64_le(1)
72}
73
74/// Recover public from signed messages.
75fn recover(input: &[u8], hash: &[u8], bit: u8) -> Result<H512, secp256k1::Error> {
76    let signature = secp256k1::Signature::parse_standard_slice(&input[64..128])?;
77    let message = secp256k1::Message::parse_slice(hash)?;
78    let recovery_id = secp256k1::RecoveryId::parse(bit)?;
79    let pub_key = secp256k1::recover(&message, &signature, &recovery_id)?;
80    let pub_key_ser = pub_key.serialize();
81    Ok(H512::from_slice(&pub_key_ser[1..65]))
82}
83
84/// ECRECOVER implemented as a native contract.
85pub struct EcRecover {}
86
87impl PrecompiledContract for EcRecover {
88    fn required_gas(&self, _: &[u8]) -> u64 {
89        G_ECRECOVER
90    }
91
92    fn run(&self, i: &[u8]) -> Result<Vec<u8>, err::Error> {
93        let len = std::cmp::min(i.len(), 128);
94
95        let mut input = [0; 128];
96        input[..len].copy_from_slice(&i[..len]);
97
98        let hash = H256::from_slice(&input[0..32]);
99        let v = H256::from_slice(&input[32..64]);
100        let r = H256::from_slice(&input[64..96]);
101        let s = H256::from_slice(&input[96..128]);
102
103        let bit = match v[31] {
104            27 | 28 if v.0[..31] == [0; 31] => v[31] - 27,
105            _ => {
106                return Ok(vec![]);
107            }
108        };
109        if !is_signature_valid(&r, &s, bit) {
110            return Ok(vec![]);
111        }
112        let mut output: Vec<u8> = Vec::new();
113        if let Ok(public) = recover(&input, hash.as_bytes(), bit) {
114            let data = common::hash::summary(&public.0);
115            output.write_all(&[0; 12])?;
116            output.write_all(&data[12..data.len()])?;
117        }
118        Ok(output)
119    }
120}
121
122/// SHA256 implemented as a native contract.
123pub struct SHA256Hash {}
124
125impl PrecompiledContract for SHA256Hash {
126    // This method does not require any overflow checking as the input size gas costs
127    // required for anything significant is so high it's impossible to pay for.
128    fn required_gas(&self, i: &[u8]) -> u64 {
129        (i.len() as u64 + 31) / 32 * G_SHA256_PER_WORD + G_SHA256_BASE
130    }
131
132    fn run(&self, i: &[u8]) -> Result<Vec<u8>, err::Error> {
133        let mut hasher = Sha256::new();
134        hasher.update(i);
135        let result = hasher.finalize();
136        let mut output: Vec<u8> = Vec::new();
137        output.write_all(&result).unwrap();
138        Ok(output)
139    }
140}
141
142/// RIPEMD160 implemented as a native contract.
143pub struct RIPEMD160Hash {}
144
145impl PrecompiledContract for RIPEMD160Hash {
146    // This method does not require any overflow checking as the input size gas costs
147    // required for anything significant is so high it's impossible to pay for.
148    fn required_gas(&self, i: &[u8]) -> u64 {
149        (i.len() as u64 + 31) / 32 * G_RIPEMD160_PER_WORD + G_RIPEMD160_BASE
150    }
151
152    fn run(&self, i: &[u8]) -> Result<Vec<u8>, err::Error> {
153        let mut hasher = Ripemd160::new();
154        hasher.update(i);
155        let result = hasher.finalize();
156        let mut output: Vec<u8> = Vec::new();
157        output.write_all(&[0; 12]).unwrap();
158        output.write_all(&result).unwrap();
159        Ok(output)
160    }
161}
162
163pub struct DataCopy {}
164
165impl PrecompiledContract for DataCopy {
166    // This method does not require any overflow checking as the input size gas costs
167    // required for anything significant is so high it's impossible to pay for.
168    fn required_gas(&self, i: &[u8]) -> u64 {
169        (i.len() as u64 + 31) / 32 * G_IDENTITY_PER_WORD + G_IDENTITY_BASE
170    }
171
172    fn run(&self, i: &[u8]) -> Result<Vec<u8>, err::Error> {
173        Ok(i.into())
174    }
175}
176
177/// BigModExp implements a native big integer exponential modular operation.
178pub struct BigModExp {}
179
180impl PrecompiledContract for BigModExp {
181    fn required_gas(&self, _: &[u8]) -> u64 {
182        0
183    }
184
185    fn run(&self, _: &[u8]) -> Result<Vec<u8>, err::Error> {
186        Err(err::Error::Str("Not implemented!".into()))
187    }
188}
189
190/// Bn256Add implements a native elliptic curve point addition.
191pub struct Bn256Add {}
192
193impl PrecompiledContract for Bn256Add {
194    fn required_gas(&self, _: &[u8]) -> u64 {
195        G_BN256_ADD
196    }
197
198    fn run(&self, _: &[u8]) -> Result<Vec<u8>, err::Error> {
199        Err(err::Error::Str("Not implemented!".into()))
200    }
201}
202
203/// Bn256ScalarMul implements a native elliptic curve point addition.
204pub struct Bn256ScalarMul {}
205
206impl PrecompiledContract for Bn256ScalarMul {
207    fn required_gas(&self, _: &[u8]) -> u64 {
208        G_BN256_SCALAR_MUL
209    }
210
211    fn run(&self, _: &[u8]) -> Result<Vec<u8>, err::Error> {
212        Err(err::Error::Str("Not implemented!".into()))
213    }
214}
215
216/// Bn256Pairing implements a pairing pre-compile for the bn256 curve
217pub struct Bn256Pairing {}
218
219impl PrecompiledContract for Bn256Pairing {
220    fn required_gas(&self, i: &[u8]) -> u64 {
221        G_BN256_PARING_BASE + (i.len() as u64 / 192 * G_BN256_PARING_PER_POINT)
222    }
223
224    fn run(&self, _: &[u8]) -> Result<Vec<u8>, err::Error> {
225        Err(err::Error::Str("Not implemented!".into()))
226    }
227}