Skip to content

Commit fdaff32

Browse files
sh-chabeer-1
andauthored
ripemd160 & bech32 (#150)
* add ripemd160 to hash module * add bech32 module * fmt * fix names * fix test to use simple hex macro --------- Co-authored-by: beer-1 <[email protected]>
1 parent ae238bf commit fdaff32

File tree

14 files changed

+271
-2
lines changed

14 files changed

+271
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ base64 = "0.21.7"
107107
bigdecimal = ">=0.4.5"
108108
bech32 = "0.11"
109109
triomphe = "0.1.9"
110+
ripemd = "0.1.1"
110111
tiny-keccak = { version = "2.0.2", features = ["keccak", "sha3"] }
111112

112113
# Note: the BEGIN and END comments below are required for external tooling. Do not remove.

crates/gas/src/initia_stdlib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ crate::macros::define_gas_parameters!(
3939
[base64_decode_base: InternalGas, "base64.decode.base", 1102],
4040
[base64_decode_unit: InternalGasPerByte, "base64.decode.unit", 18],
4141

42+
[bech32_encode_base: InternalGas, "bech32.encode.base", 1102],
43+
[bech32_encode_unit: InternalGasPerByte, "bech32.encode.unit", 18],
44+
[bech32_decode_base: InternalGas, "bech32.decode.base", 1102],
45+
[bech32_decode_unit: InternalGasPerByte, "bech32.decode.unit", 18],
46+
4247
[crypto_ed25519_base: InternalGas, "crypto.ed25519.base", 551],
4348
[crypto_ed25519_per_sig_verify: InternalGasPerArg, "crypto.ed25519.per_sig_verify", 981492],
4449
[crypto_ed25519_per_pubkey_deserialize: InternalGasPerArg, "crypto.ed25519.per_pubkey_deserialize", 139688],

crates/gas/src/move_stdlib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ crate::macros::define_gas_parameters!(
1616
[hash_sha2_256_per_byte: InternalGasPerByte, "hash.sha2_256.per_byte", 183],
1717
[hash_sha3_256_base: InternalGas, "hash.sha3_256.base", 14704],
1818
[hash_sha3_256_per_byte: InternalGasPerByte, "hash.sha3_256.per_byte", 165],
19+
[hash_ripemd160_base: InternalGas, "hash.ripemd160.base", 11028],
20+
[hash_ripemd160_per_byte: InternalGasPerByte, "hash.ripemd160.per_byte", 183],
1921

2022
// Note(Gas): this initial value is guesswork.
2123
[signer_borrow_address_base: InternalGas, "signer.borrow_address.base", 735],

crates/natives/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ rand_core = { workspace = true }
2525
libsecp256k1 = { workspace = true }
2626
sha2 = { workspace = true }
2727
sha3 = { workspace = true }
28+
ripemd = { workspace = true }
2829
ed25519-consensus = { workspace = true }
2930
hex = { workspace = true }
3031

crates/natives/src/bech32.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
use bech32::{Bech32, Hrp};
2+
use move_core_types::gas_algebra::NumBytes;
3+
use move_vm_runtime::native_functions::NativeFunction;
4+
use move_vm_types::{
5+
loaded_data::runtime_types::Type,
6+
values::{Struct, Value},
7+
};
8+
use smallvec::{smallvec, SmallVec};
9+
use std::collections::VecDeque;
10+
11+
use crate::{
12+
helpers::get_string,
13+
interface::{
14+
RawSafeNative, SafeNativeBuilder, SafeNativeContext, SafeNativeError, SafeNativeResult,
15+
},
16+
safely_pop_arg,
17+
};
18+
19+
// See stdlib/error.move
20+
const ECATEGORY_INVALID_ARGUMENT: u64 = 0x1;
21+
22+
// native errors always start from 100
23+
const EUNABLE_TO_ENCODE: u64 = (ECATEGORY_INVALID_ARGUMENT << 16) + 100;
24+
const EUNABLE_TO_DECODE: u64 = (ECATEGORY_INVALID_ARGUMENT << 16) + 101;
25+
const EINVALID_PREFIX: u64 = (ECATEGORY_INVALID_ARGUMENT << 16) + 102;
26+
const EINVALID_ADDRESS: u64 = (ECATEGORY_INVALID_ARGUMENT << 16) + 103;
27+
28+
/*
29+
native public fun encode(prefix: String, data: vector<u8>): String;
30+
native public fun decode(addr: String): (String, vector<u8>);
31+
*/
32+
33+
/***************************************************************************************************
34+
* native fun encode
35+
*
36+
* gas cost: base_cost + unit_cost * (prefix_len + data_len)
37+
*
38+
**************************************************************************************************/
39+
fn native_encode(
40+
context: &mut SafeNativeContext,
41+
ty_args: Vec<Type>,
42+
mut arguments: VecDeque<Value>,
43+
) -> SafeNativeResult<SmallVec<[Value; 1]>> {
44+
let gas_params = &context.native_gas_params.initia_stdlib;
45+
46+
debug_assert!(ty_args.is_empty());
47+
debug_assert_eq!(arguments.len(), 2);
48+
49+
let data = safely_pop_arg!(arguments, Vec<u8>);
50+
let raw_prefix = get_string(safely_pop_arg!(arguments, Struct))?;
51+
let prefix = String::from_utf8(raw_prefix).map_err(|_| SafeNativeError::Abort {
52+
abort_code: EINVALID_PREFIX,
53+
})?;
54+
context.charge(
55+
gas_params.bech32_encode_base
56+
+ gas_params.bech32_encode_unit * NumBytes::new((prefix.len() + data.len()) as u64),
57+
)?;
58+
59+
let encoded_string = bech32::encode::<Bech32>(
60+
Hrp::parse(prefix.as_str()).map_err(|_| SafeNativeError::Abort {
61+
abort_code: EINVALID_PREFIX,
62+
})?,
63+
data.as_slice(),
64+
)
65+
.map_err(|_| SafeNativeError::Abort {
66+
abort_code: EUNABLE_TO_ENCODE,
67+
})?;
68+
69+
Ok(smallvec![Value::struct_(Struct::pack(vec![
70+
Value::vector_u8(encoded_string.as_bytes().to_vec()),
71+
]))])
72+
}
73+
74+
/***************************************************************************************************
75+
* native fun decode
76+
*
77+
* gas cost: base_cost + unit_cost * address_len
78+
*
79+
**************************************************************************************************/
80+
fn native_decode(
81+
context: &mut SafeNativeContext,
82+
ty_args: Vec<Type>,
83+
mut arguments: VecDeque<Value>,
84+
) -> SafeNativeResult<SmallVec<[Value; 1]>> {
85+
let gas_params = &context.native_gas_params.initia_stdlib;
86+
87+
debug_assert!(ty_args.is_empty());
88+
debug_assert_eq!(arguments.len(), 1);
89+
90+
let raw_addr = get_string(safely_pop_arg!(arguments, Struct))?;
91+
let addr = String::from_utf8(raw_addr).map_err(|_| SafeNativeError::Abort {
92+
abort_code: EINVALID_ADDRESS,
93+
})?;
94+
95+
context.charge(
96+
gas_params.bech32_decode_base
97+
+ gas_params.bech32_decode_unit * NumBytes::new(addr.len() as u64),
98+
)?;
99+
100+
let (prefix, words) = bech32::decode(addr.as_str()).map_err(|_| SafeNativeError::Abort {
101+
abort_code: EUNABLE_TO_DECODE,
102+
})?;
103+
104+
Ok(smallvec![
105+
Value::struct_(Struct::pack(vec![Value::vector_u8(
106+
prefix.as_bytes().to_vec()
107+
)])),
108+
Value::vector_u8(words)
109+
])
110+
}
111+
112+
/***************************************************************************************************
113+
* module
114+
*
115+
**************************************************************************************************/
116+
pub fn make_all(
117+
builder: &SafeNativeBuilder,
118+
) -> impl Iterator<Item = (String, NativeFunction)> + '_ {
119+
let natives = [
120+
("encode", native_encode as RawSafeNative),
121+
("decode", native_decode),
122+
];
123+
124+
builder.make_named_natives(natives)
125+
}

crates/natives/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub mod account;
88
pub mod address;
99
pub mod any;
1010
pub mod base64;
11+
pub mod bech32;
1112
pub mod biguint;
1213
pub mod block;
1314
pub mod code;
@@ -62,6 +63,7 @@ pub fn initia_move_natives(
6263
add_natives_from_module!("type_info", type_info::make_all(builder));
6364
add_natives_from_module!("from_bcs", from_bcs::make_all(builder));
6465
add_natives_from_module!("base64", base64::make_all(builder));
66+
add_natives_from_module!("bech32", bech32::make_all(builder));
6567
add_natives_from_module!("keccak", keccak::make_all(builder));
6668
add_natives_from_module!("staking", staking::make_all(builder));
6769
add_natives_from_module!("cosmos", cosmos::make_all(builder));

crates/natives/src/move_stdlib/hash.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{
2020
/***************************************************************************************************
2121
* native fun sha2_256
2222
*
23-
* gas cost: base_cost + unit_cost * max(input_length_in_bytes, legacy_min_input_len)
23+
* gas cost: base_cost + unit_cost * input_length_in_bytes
2424
*
2525
**************************************************************************************************/
2626
#[inline]
@@ -48,7 +48,7 @@ fn native_sha2_256(
4848
/***************************************************************************************************
4949
* native fun sha3_256
5050
*
51-
* gas cost: base_cost + unit_cost * max(input_length_in_bytes, legacy_min_input_len)
51+
* gas cost: base_cost + unit_cost * input_length_in_bytes
5252
*
5353
**************************************************************************************************/
5454
#[inline]
@@ -73,6 +73,37 @@ fn native_sha3_256(
7373
Ok(smallvec![Value::vector_u8(hash_vec)])
7474
}
7575

76+
/***************************************************************************************************
77+
* native fun ripemd160
78+
*
79+
* gas cost: base_cost + unit_cost * input_length_in_bytes
80+
*
81+
**************************************************************************************************/
82+
#[inline]
83+
fn native_ripemd160(
84+
context: &mut SafeNativeContext,
85+
_ty_args: Vec<Type>,
86+
mut arguments: VecDeque<Value>,
87+
) -> SafeNativeResult<SmallVec<[Value; 1]>> {
88+
let gas_params = &context.native_gas_params.move_stdlib;
89+
90+
debug_assert!(_ty_args.is_empty());
91+
debug_assert!(arguments.len() == 1);
92+
93+
let hash_arg = safely_pop_arg!(arguments, Vec<u8>);
94+
95+
context.charge(
96+
gas_params.hash_ripemd160_base
97+
+ gas_params.hash_ripemd160_per_byte * NumBytes::new(hash_arg.len() as u64),
98+
)?;
99+
100+
let mut hasher = ripemd::Ripemd160::new();
101+
hasher.update(&hash_arg);
102+
let hash_vec = hasher.finalize().to_vec();
103+
104+
Ok(smallvec![Value::vector_u8(hash_vec)])
105+
}
106+
76107
/***************************************************************************************************
77108
* module
78109
**************************************************************************************************/
@@ -82,6 +113,7 @@ pub fn make_all(
82113
let natives = [
83114
("sha2_256", native_sha2_256 as RawSafeNative),
84115
("sha3_256", native_sha3_256),
116+
("ripemd160", native_ripemd160),
85117
];
86118

87119
builder.make_named_natives(natives)

precompile/binaries/minlib/bech32.mv

168 Bytes
Binary file not shown.

precompile/binaries/minlib/hash.mv

20 Bytes
Binary file not shown.

precompile/binaries/stdlib/bech32.mv

168 Bytes
Binary file not shown.

precompile/binaries/stdlib/hash.mv

20 Bytes
Binary file not shown.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module initia_std::bech32 {
2+
use initia_std::string::String;
3+
4+
native public fun encode(prefix: String, data: vector<u8>): String;
5+
native public fun decode(addr: String): (String, vector<u8>);
6+
7+
#[test_only]
8+
use initia_std::string;
9+
10+
#[test]
11+
fun test_bech32_encode() {
12+
let prefix = string::utf8(b"init");
13+
let data = x"12eafdba79c3dd7b90e3712ee475423153a722c7";
14+
let got = encode(prefix, data);
15+
let expected = string::utf8(b"init1zt40mwnec0whhy8rwyhwga2zx9f6wgk8p3x098");
16+
assert!(got == expected, 0);
17+
18+
let prefix = string::utf8(b"celestia");
19+
let data = x"12eafdba79c3dd7b90e3712ee475423153a722c7";
20+
let got = encode(prefix, data);
21+
let expected = string::utf8(b"celestia1zt40mwnec0whhy8rwyhwga2zx9f6wgk87dhv5g");
22+
assert!(got == expected, 1);
23+
}
24+
25+
#[test]
26+
fun test_bech32_decode() {
27+
let addr = string::utf8(b"init1zt40mwnec0whhy8rwyhwga2zx9f6wgk8p3x098");
28+
let (prefix, data) = decode(addr);
29+
assert!(prefix == string::utf8(b"init"), 0);
30+
assert!(data == x"12eafdba79c3dd7b90e3712ee475423153a722c7", 1);
31+
32+
let addr = string::utf8(b"celestia1zt40mwnec0whhy8rwyhwga2zx9f6wgk87dhv5g");
33+
let (prefix, data) = decode(addr);
34+
assert!(prefix == string::utf8(b"celestia"), 2);
35+
assert!(data == x"12eafdba79c3dd7b90e3712ee475423153a722c7", 3);
36+
}
37+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module minitia_std::bech32 {
2+
use minitia_std::string::String;
3+
4+
native public fun encode(prefix: String, data: vector<u8>): String;
5+
native public fun decode(addr: String): (String, vector<u8>);
6+
7+
#[test_only]
8+
use minitia_std::string;
9+
10+
#[test]
11+
fun test_bech32_encode() {
12+
let prefix = string::utf8(b"init");
13+
let data = x"12eafdba79c3dd7b90e3712ee475423153a722c7";
14+
let got = encode(prefix, data);
15+
let expected = string::utf8(b"init1zt40mwnec0whhy8rwyhwga2zx9f6wgk8p3x098");
16+
assert!(got == expected, 0);
17+
18+
let prefix = string::utf8(b"celestia");
19+
let data = x"12eafdba79c3dd7b90e3712ee475423153a722c7";
20+
let got = encode(prefix, data);
21+
let expected = string::utf8(b"celestia1zt40mwnec0whhy8rwyhwga2zx9f6wgk87dhv5g");
22+
assert!(got == expected, 1);
23+
}
24+
25+
#[test]
26+
fun test_bech32_decode() {
27+
let addr = string::utf8(b"init1zt40mwnec0whhy8rwyhwga2zx9f6wgk8p3x098");
28+
let (prefix, data) = decode(addr);
29+
assert!(prefix == string::utf8(b"init"), 0);
30+
assert!(data == x"12eafdba79c3dd7b90e3712ee475423153a722c7", 1);
31+
32+
let addr = string::utf8(b"celestia1zt40mwnec0whhy8rwyhwga2zx9f6wgk87dhv5g");
33+
let (prefix, data) = decode(addr);
34+
assert!(prefix == string::utf8(b"celestia"), 2);
35+
assert!(data == x"12eafdba79c3dd7b90e3712ee475423153a722c7", 3);
36+
}
37+
}

precompile/modules/move_stdlib/sources/hash.move

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,31 @@
55
module std::hash {
66
native public fun sha2_256(data: vector<u8>): vector<u8>;
77
native public fun sha3_256(data: vector<u8>): vector<u8>;
8+
native public fun ripemd160(data: vector<u8>): vector<u8>;
9+
10+
#[test]
11+
fun ripemd160_test() {
12+
let inputs = vector[
13+
b"testing",
14+
b"",
15+
];
16+
17+
// From https://www.browserling.com/tools/ripemd160-hash
18+
let outputs = vector[
19+
x"b89ba156b40bed29a5965684b7d244c49a3a769b",
20+
x"9c1185a5c5e9fc54612808977ee8f548b2258d31",
21+
];
22+
23+
let i = 0;
24+
while (i < std::vector::length(&inputs)) {
25+
let input = *std::vector::borrow(&inputs, i);
26+
let hash_expected = *std::vector::borrow(&outputs, i);
27+
let hash = ripemd160(input);
28+
29+
assert!(hash_expected == hash, 1);
30+
31+
i = i + 1;
32+
};
33+
}
834
}
35+

0 commit comments

Comments
 (0)