From f0fadb9b4c186e848e56b362f539be96e407efe8 Mon Sep 17 00:00:00 2001 From: feliciss <22887031+feliciss@users.noreply.github.com> Date: Thu, 25 Jan 2024 10:47:10 +0900 Subject: [PATCH] [gh-1314] refactor crypto encoding hash functions (#1315) * [gh-1314] generalize base58 and bech32 in rust. * [gh-1314] add decode algorithm. * [gh-1314] adjust encode & decode lengths. * [gh-1314] add checksum to bs58 encode function. * [gh-1314] update docs. --------- Co-authored-by: Feliciss <10203-feliciss@users.noreply.0xacab.org> --- Cargo.lock | 13 +- Cargo.toml | 1 - crates/rooch-framework/Cargo.toml | 3 +- crates/rooch-framework/doc/decoding.md | 24 +++- crates/rooch-framework/doc/encoding.md | 40 +------ .../sources/crypto/decoding.move | 43 ++++++- .../sources/crypto/encoding.move | 71 +++-------- .../src/natives/gas_parameter/decoding.rs | 2 + .../src/natives/gas_parameter/encoding.rs | 6 +- .../src/natives/gas_parameter/native.rs | 27 ----- .../rooch_framework/crypto/decoding.rs | 49 ++++++++ .../rooch_framework/crypto/encoding.rs | 111 ++---------------- crates/rooch-types/src/address.rs | 9 +- .../pages/docs/user-guides/hash.en-US.mdx | 6 +- .../pages/docs/user-guides/hash.zh-CN.mdx | 4 +- frameworks/bitcoin-move/Cargo.toml | 1 - 16 files changed, 151 insertions(+), 259 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 135c16ae83..c5a5bcdd6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -881,15 +881,6 @@ dependencies = [ "serde 1.0.193", ] -[[package]] -name = "bitcoin-bech32" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f764aae1214365b0317fa9d7c99e17e0ebf226200850767e4cd1fb84eec4087" -dependencies = [ - "bech32 0.9.1", -] - [[package]] name = "bitcoin-internals" version = "0.2.0" @@ -907,7 +898,6 @@ dependencies = [ "bcs", "better_any", "bitcoin 0.31.0", - "bitcoin-bech32", "brotli", "bs58 0.5.0", "fastcrypto", @@ -8140,9 +8130,8 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs", + "bech32 0.9.1", "better_any", - "bitcoin 0.31.0", - "bitcoin-bech32", "brotli", "bs58 0.5.0", "fastcrypto", diff --git a/Cargo.toml b/Cargo.toml index 80ce96a64c..3c79c7a19c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -212,7 +212,6 @@ tower-http = { version = "0.3.4", features = ["cors", "full", "trace", "set-head mirai-annotations = "1.12.0" lru = "0.11.0" accumulator = { path = "moveos/moveos-commons/accumulator" } -bitcoin-bech32 = "0.13.0" bs58 = "0.5.0" dirs-next = "2.0.0" anstream = { version = "0.3" } diff --git a/crates/rooch-framework/Cargo.toml b/crates/rooch-framework/Cargo.toml index 28e4ea6c1b..9da554da13 100644 --- a/crates/rooch-framework/Cargo.toml +++ b/crates/rooch-framework/Cargo.toml @@ -26,8 +26,7 @@ serde_bytes = { workspace = true } sha3 = { workspace = true } smallvec = { workspace = true } hex = { workspace = true } -bitcoin = { workspace = true } -bitcoin-bech32 = { workspace = true } +bech32 = { workspace = true } bs58 = { workspace = true, features = ["check"] } http = { workspace = true } tracing = { workspace = true } diff --git a/crates/rooch-framework/doc/decoding.md b/crates/rooch-framework/doc/decoding.md index ba0ff4487d..5ec73d5f92 100644 --- a/crates/rooch-framework/doc/decoding.md +++ b/crates/rooch-framework/doc/decoding.md @@ -9,6 +9,7 @@ Module which defines decoding functions. - [Constants](#@Constants_0) - [Function `base58`](#0x3_decoding_base58) - [Function `base58check`](#0x3_decoding_base58check) +- [Function `bech32`](#0x3_decoding_bech32)
@@ -34,8 +35,8 @@ Failed to decode an address ## Function `base58` -@param encoded_address_bytes: encoded Bitcoin address bytes on the Bitcoin network -Decode the Bitcoin address bytes with Base58 algorithm and returns a raw address bytes +@param encoded_address_bytes: encoded base58 address bytes +Decode the base58 address bytes with Base58 algorithm and returns a raw base58 address bytes
public fun base58(encoded_address_bytes: &vector<u8>): vector<u8>
@@ -47,10 +48,23 @@ Decode the Bitcoin address bytes with Base58 algorithm and returns a raw address
 
 ## Function `base58check`
 
-@param encoded_address_bytes: encoded Bitcoin address bytes on the Bitcoin network
-@param version_byte: version byte used on Bitcoin network for verification of different types of addresses
-Decode the Bitcoin address bytes with Base58Check algorithm and returns a raw address bytes without checksum
+@param encoded_address_bytes: encoded base58 address bytes
+@param version_byte: version byte used for verification of different types of base58 addresses
+Decode the base58 address bytes with Base58Check algorithm and returns a raw address bytes without checksum
 
 
 
public fun base58check(encoded_address_bytes: &vector<u8>, version_byte: u8): vector<u8>
 
+ + + + + +## Function `bech32` + +@param encoded_bech32_address_bytes: 42 or 62 length Bech32 or Bech32m addresses +Decode the encoded 42 or 62 length Bech32 or Bech32m addresses with Bech32 or Bech32m decoding algorithm and returns 20 or 32 bytes of public keys. + + +
public fun bech32(encoded_bech32_address_bytes: &vector<u8>): vector<u8>
+
diff --git a/crates/rooch-framework/doc/encoding.md b/crates/rooch-framework/doc/encoding.md index 37e0f88139..96895c9d78 100644 --- a/crates/rooch-framework/doc/encoding.md +++ b/crates/rooch-framework/doc/encoding.md @@ -10,8 +10,6 @@ Module which defines encoding functions. - [Function `base58`](#0x3_encoding_base58) - [Function `base58check`](#0x3_encoding_base58check) - [Function `bech32`](#0x3_encoding_bech32) -- [Function `p2sh`](#0x3_encoding_p2sh) -- [Function `p2pkh`](#0x3_encoding_p2pkh)
@@ -67,8 +65,8 @@ Invalid script version ## Function `base58` -@param address_bytes: address bytes on the Bitcoin network -Encode the address bytes with Base58 algorithm and returns an encoded Bitcoin address +@param address_bytes: address bytes for base58 format +Encode the address bytes with Base58 algorithm and returns an encoded base58 bytes
public fun base58(address_bytes: &vector<u8>): vector<u8>
@@ -80,9 +78,9 @@ Encode the address bytes with Base58 algorithm and returns an encoded Bitcoin ad
 
 ## Function `base58check`
 
-@param address_bytes: address bytes on the Bitcoin network
-@param version_byte: version byte used on Bitcoin network for verification of different types of addresses
-Encode the address bytes with Base58Check algorithm and returns an encoded Bitcoin address with checksum
+@param address_bytes: address bytes on the base58 checksum format
+@param version_byte: version byte used for verification of different types of checksum addresses
+Encode the address bytes with Base58Check algorithm and returns an encoded base58 bytes with checksum
 
 
 
public fun base58check(address_bytes: &vector<u8>, version_byte: u8): vector<u8>
@@ -96,34 +94,8 @@ Encode the address bytes with Base58Check algorithm and returns an encoded Bitco
 
 @param public_key: 20 or 32 bytes public keys
 @param version: 0 for bech32 encoding and 1 for bech32m encoding. 2-16 are held.
-Encode the public key with Bech32 or Bech32m encoding algorithm and returns 42 or 62 length Bitcoin Bech32 address.
+Encode the public keys with Bech32 or Bech32m encoding algorithm and returns 42 or 62 length Bech32 or Bech32m addresses.
 
 
 
public fun bech32(public_key: &vector<u8>, version: u8): vector<u8>
 
- - - - - -## Function `p2sh` - -@param public_key: 33 bytes compressed public key -Creates a pay to script hash P2SH address from a script converted from a compressed public key. - - -
public fun p2sh(public_key: &vector<u8>): vector<u8>
-
- - - - - -## Function `p2pkh` - -@param public_key: 33 bytes compressed public key -Creates a pay to (compressed) public key hash address from a public key. - - -
public fun p2pkh(public_key: &vector<u8>): vector<u8>
-
diff --git a/crates/rooch-framework/sources/crypto/decoding.move b/crates/rooch-framework/sources/crypto/decoding.move index 894c25ac64..f174ea91f0 100644 --- a/crates/rooch-framework/sources/crypto/decoding.move +++ b/crates/rooch-framework/sources/crypto/decoding.move @@ -7,15 +7,19 @@ module rooch_framework::decoding { /// Failed to decode an address const ErrorDecodeFailed: u64 = 1; - /// @param encoded_address_bytes: encoded Bitcoin address bytes on the Bitcoin network - /// Decode the Bitcoin address bytes with Base58 algorithm and returns a raw address bytes + /// @param encoded_address_bytes: encoded base58 address bytes + /// Decode the base58 address bytes with Base58 algorithm and returns a raw base58 address bytes native public fun base58(encoded_address_bytes: &vector): vector; - /// @param encoded_address_bytes: encoded Bitcoin address bytes on the Bitcoin network - /// @param version_byte: version byte used on Bitcoin network for verification of different types of addresses - /// Decode the Bitcoin address bytes with Base58Check algorithm and returns a raw address bytes without checksum + /// @param encoded_address_bytes: encoded base58 address bytes + /// @param version_byte: version byte used for verification of different types of base58 addresses + /// Decode the base58 address bytes with Base58Check algorithm and returns a raw address bytes without checksum native public fun base58check(encoded_address_bytes: &vector, version_byte: u8): vector; + /// @param encoded_bech32_address_bytes: 42 or 62 length Bech32 or Bech32m addresses + /// Decode the encoded 42 or 62 length Bech32 or Bech32m addresses with Bech32 or Bech32m decoding algorithm and returns 20 or 32 bytes of public keys. + native public fun bech32(encoded_bech32_address_bytes: &vector): vector; + #[test] /// This test can be verified at http://lenschulwitz.com/base58. fun test_base58_decoding() { @@ -35,4 +39,33 @@ module rooch_framework::decoding { assert!(decoded_address_bytes == expected_decoded_address_bytes, 1001); } + + #[test] + fun test_bech32_decoding_to_52_public_key() { + let encoded_address = b"bech321qvdcf32k0vfxgsyet5ldt246q4jaw8scx3sysx0lnstlt6w4m5rsl7wnyw"; + // TODO handle bech32 and bech32m public key's difference + let expected_public_key = bech32(&encoded_address); + + let public_key = x"000c0d1809110a160f0c0906081004190b141f0d0b0a151a0015121d0e0710180611100410060f1f13100b1f0b1a0e151b140310"; // 52-bytes raw public key for a Bech32 address + assert!(public_key == expected_public_key, 1002); + } + + #[test] + fun test_bech32_decoding_to_32_public_key() { + let encoded_address = b"bech321q302rl92lgujmvlk5h6pf63zn6wekcjqxj30hr"; + let expected_public_key = bech32(&encoded_address); + + let public_key = x"00110f0a031f050a1f081c121b0c1f1614171a01091a1102131a0e1916181200"; // 32-bytes raw public key for a Bech32 address + assert!(public_key == expected_public_key, 1003); + } + + #[test] + fun test_bech32_decoding_to_52_bech32m_public_key() { + let encoded_address = b"bech32m1qvdcf32k0vfxgsyet5ldt246q4jaw8scx3sysx0lnstlt6w4m5rs6l85rk"; + // TODO handle bech32 and bech32m public key's difference + let expected_public_key = bech32(&encoded_address); + + let public_key = x"000c0d1809110a160f0c0906081004190b141f0d0b0a151a0015121d0e0710180611100410060f1f13100b1f0b1a0e151b140310"; // 52-bytes public key for a Bech32m address + assert!(public_key == expected_public_key, 1004); + } } diff --git a/crates/rooch-framework/sources/crypto/encoding.move b/crates/rooch-framework/sources/crypto/encoding.move index 0b681cdba5..d1763a439e 100644 --- a/crates/rooch-framework/sources/crypto/encoding.move +++ b/crates/rooch-framework/sources/crypto/encoding.move @@ -16,28 +16,20 @@ module rooch_framework::encoding { /// Invalid script version const ErrorInvalidScriptVersion: u64 = 4; - /// @param address_bytes: address bytes on the Bitcoin network - /// Encode the address bytes with Base58 algorithm and returns an encoded Bitcoin address + /// @param address_bytes: address bytes for base58 format + /// Encode the address bytes with Base58 algorithm and returns an encoded base58 bytes native public fun base58(address_bytes: &vector): vector; - /// @param address_bytes: address bytes on the Bitcoin network - /// @param version_byte: version byte used on Bitcoin network for verification of different types of addresses - /// Encode the address bytes with Base58Check algorithm and returns an encoded Bitcoin address with checksum + /// @param address_bytes: address bytes on the base58 checksum format + /// @param version_byte: version byte used for verification of different types of checksum addresses + /// Encode the address bytes with Base58Check algorithm and returns an encoded base58 bytes with checksum native public fun base58check(address_bytes: &vector, version_byte: u8): vector; /// @param public_key: 20 or 32 bytes public keys /// @param version: 0 for bech32 encoding and 1 for bech32m encoding. 2-16 are held. - /// Encode the public key with Bech32 or Bech32m encoding algorithm and returns 42 or 62 length Bitcoin Bech32 address. + /// Encode the public keys with Bech32 or Bech32m encoding algorithm and returns 42 or 62 length Bech32 or Bech32m addresses. native public fun bech32(public_key: &vector, version: u8): vector; - /// @param public_key: 33 bytes compressed public key - /// Creates a pay to script hash P2SH address from a script converted from a compressed public key. - native public fun p2sh(public_key: &vector): vector; - - /// @param public_key: 33 bytes compressed public key - /// Creates a pay to (compressed) public key hash address from a public key. - native public fun p2pkh(public_key: &vector): vector; - #[test] /// This test can be verified at http://lenschulwitz.com/base58. fun test_base58_encoding() { @@ -69,62 +61,35 @@ module rooch_framework::encoding { } #[test] - /// This test is verified at https://www.blockchain.com/explorer/addresses/btc/bc1qqvdcf32k0vfxgsyet5ldt246q4jaw8scx3sysx0lnstlt6w4m5rsgej0cd. - fun test_bech32_encoding_to_p2wsh_address() { - let public_key = x"031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd07"; // 32-bytes public key for a Bech32 (P2WSH) address - let version = 0; // version 0 for a Bech32 (P2WSH) address + fun test_bech32_encoding_with_32_public_key() { + let public_key = x"031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd07"; // 32-bytes public key for a Bech32 address + let version = 0; // version 0 for a Bech32 address let encoded_address = bech32(&public_key, version); - let expected_encoded_address = b"bc1qqvdcf32k0vfxgsyet5ldt246q4jaw8scx3sysx0lnstlt6w4m5rsgej0cd"; + let expected_encoded_address = b"bech321qvdcf32k0vfxgsyet5ldt246q4jaw8scx3sysx0lnstlt6w4m5rsl7wnyw"; assert!(encoded_address == expected_encoded_address, 1002); } #[test] - /// This test is verified at https://www.blockchain.com/explorer/addresses/btc/bc1qq302rl92lgujmvlk5h6pf63zn6wekcjql6cmum. - fun test_bech32_encoding_to_p2wpkh_address() { - let public_key = x"045ea1fcaafa392db3f6a5f414ea229e9d9b6240"; // 20-bytes public key for a Bech32 (P2WPKH) address - let version = 0; // version 0 for a Bech32 (P2WPKH) address + fun test_bech32_encoding_with_20_public_key() { + let public_key = x"045ea1fcaafa392db3f6a5f414ea229e9d9b6240"; // 20-bytes public key for a Bech32 address + let version = 0; // version 0 for a Bech32 address let encoded_address = bech32(&public_key, version); - let expected_encoded_address = b"bc1qq302rl92lgujmvlk5h6pf63zn6wekcjql6cmum"; + let expected_encoded_address = b"bech321q302rl92lgujmvlk5h6pf63zn6wekcjqxj30hr"; assert!(encoded_address == expected_encoded_address, 1003); } #[test] - /// This test is verified at https://www.blockchain.com/explorer/addresses/btc/bc1pqvdcf32k0vfxgsyet5ldt246q4jaw8scx3sysx0lnstlt6w4m5rszwjxq3. - fun test_bech32_encoding_to_p2tr_address() { - let public_key = x"031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd07"; // 32-bytes public key for a Bech32 (Taproot) address - let version = 1; // version 1 for a Bech32 (Taproot) address + fun test_bech32_encoding_with_version_1_32_public_key() { + let public_key = x"031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd07"; // 32-bytes public key for a Bech32m address + let version = 1; // version 1 for a Bech32m address let encoded_address = bech32(&public_key, version); - let expected_encoded_address = b"bc1pqvdcf32k0vfxgsyet5ldt246q4jaw8scx3sysx0lnstlt6w4m5rszwjxq3"; + let expected_encoded_address = b"bech32m1qvdcf32k0vfxgsyet5ldt246q4jaw8scx3sysx0lnstlt6w4m5rs6l85rk"; assert!(encoded_address == expected_encoded_address, 1004); } - - // Test function for P2SH address generation - #[test] - /// This test is verified at https://www.blockchain.com/explorer/addresses/btc/3FrvSxCNmbGbYxvaBY9rhTWKauCXWPpLh9. - fun test_p2sh_address() { - let public_key = x"0345c567b17d2e69c8b91b3503f0fb50ebb88f1806e9133f62d3d3501efcedf8d3"; // 33-byte compressed public key - let p2sh_address = p2sh(&public_key); - - let expected_p2sh_address = b"3FrvSxCNmbGbYxvaBY9rhTWKauCXWPpLh9"; // Sample P2SH address - - assert!(p2sh_address == expected_p2sh_address, 1005); - } - - // Test function for P2PKH address generation - #[test] - /// This test is verified at https://www.blockchain.com/explorer/addresses/btc/1Dnv7vKt2JEK7tQnbmYBq3Sfuckz7wFS6e. - fun test_p2pkh_address() { - let public_key = x"03a819b6f0eb5f22167fffa53e1628cfbf645db9a4c50b3a226e5d20c9984e63a2"; // 33-byte compressed public key - let p2pkh_address = p2pkh(&public_key); - - let expected_p2pkh_address = b"1Dnv7vKt2JEK7tQnbmYBq3Sfuckz7wFS6e"; // Sample P2PKH address - - assert!(p2pkh_address == expected_p2pkh_address, 1006); - } } diff --git a/crates/rooch-framework/src/natives/gas_parameter/decoding.rs b/crates/rooch-framework/src/natives/gas_parameter/decoding.rs index d7257455ba..c1c40a652e 100644 --- a/crates/rooch-framework/src/natives/gas_parameter/decoding.rs +++ b/crates/rooch-framework/src/natives/gas_parameter/decoding.rs @@ -9,4 +9,6 @@ crate::natives::gas_parameter::native::define_gas_parameters_for_natives!(GasPar [.base58.per_byte, "base58.per_byte", 30 * MUL], [.base58check.base, "base58check.base", 1000 * MUL], [.base58check.per_byte, "base58check.per_byte", 30 * MUL], + [.bech32.base, "bech32.base", 1000 * MUL], + [.bech32.per_byte, "bech32.per_byte", 30 * MUL] ]); diff --git a/crates/rooch-framework/src/natives/gas_parameter/encoding.rs b/crates/rooch-framework/src/natives/gas_parameter/encoding.rs index 9d25be7393..9d7271d0a5 100644 --- a/crates/rooch-framework/src/natives/gas_parameter/encoding.rs +++ b/crates/rooch-framework/src/natives/gas_parameter/encoding.rs @@ -10,9 +10,5 @@ crate::natives::gas_parameter::native::define_gas_parameters_for_natives!(GasPar [.base58check.base, "base58check.base", 1000 * MUL], [.base58check.per_byte, "base58check.per_byte", 30 * MUL], [.bech32.base, "bech32.base", 1000 * MUL], - [.bech32.per_byte, "bech32.per_byte", 30 * MUL], - [.p2pkh.base, "p2pkh.base", 1000 * MUL], - [.p2pkh.per_byte, "p2pkh.per_byte", 30 * MUL], - [.p2sh.base, "p2sh.base", 1000 * MUL], - [.p2sh.per_byte, "p2sh.per_byte", 30 * MUL], + [.bech32.per_byte, "bech32.per_byte", 30 * MUL] ]); diff --git a/crates/rooch-framework/src/natives/gas_parameter/native.rs b/crates/rooch-framework/src/natives/gas_parameter/native.rs index 37e4280e08..052396027c 100644 --- a/crates/rooch-framework/src/natives/gas_parameter/native.rs +++ b/crates/rooch-framework/src/natives/gas_parameter/native.rs @@ -74,28 +74,6 @@ macro_rules! expand_kv_for_native_gas_params { } } -#[cfg(test)] -macro_rules! extract_key_for_native_gas_params { - (test_only $(.$field: ident)+, $(optional)? $key: literal, $initial_val: expr) => { - #[cfg(feature = "testing")] - $key - }; - ($(.$field: ident)+, $(optional)? $key: literal, $initial_val: expr) => { - $key - }; -} - -#[cfg(test)] -macro_rules! extract_path_for_native_gas_params { - (test_only $(.$field: ident)+, $(optional)? $key: literal, $initial_val: expr) => { - #[cfg(feature = "testing")] - stringify!($($field).*) - }; - ($(.$field: ident)+, $(optional)? $key: literal, $initial_val: expr) => { - stringify!($($field).*) - }; -} - #[macro_export] macro_rules! define_gas_parameters_for_natives { ($param_ty: ty, $package_name: literal, [$([$($t: tt)*]),* $(,)?] $(, allow_unmapped = $allow_unmapped: expr)?) => { @@ -145,11 +123,6 @@ macro_rules! define_gas_parameters_for_natives { }; } -#[cfg(test)] -pub(crate) use extract_key_for_native_gas_params; -#[cfg(test)] -pub(crate) use extract_path_for_native_gas_params; - pub use define_gas_parameters_for_natives; pub use expand_get_for_native_gas_params; pub use expand_get_impl_for_native_gas_params; diff --git a/crates/rooch-framework/src/natives/rooch_framework/crypto/decoding.rs b/crates/rooch-framework/src/natives/rooch_framework/crypto/decoding.rs index c57f8fc429..f16e620c12 100644 --- a/crates/rooch-framework/src/natives/rooch_framework/crypto/decoding.rs +++ b/crates/rooch-framework/src/natives/rooch_framework/crypto/decoding.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::natives::helpers::{make_module_natives, make_native}; +use bech32::Variant; use move_binary_format::errors::PartialVMResult; use move_core_types::gas_algebra::{InternalGas, InternalGasPerByte, NumBytes}; use move_vm_runtime::native_functions::{NativeContext, NativeFunction}; @@ -85,6 +86,51 @@ pub fn native_base58check( )) } +/*************************************************************************************************** + * native fun bech32 + * Implementation of the Move native function `encoding::bech32(public_key: &vector, version: u8): vector` + * gas cost: encoding_bech32_cost_base | base cost for function call and fixed opers + * + encoding_bech32_data_cost_per_byte * msg.len() | cost depends on length of message + * + encoding_bech32_data_cost_per_block * num_blocks | cost depends on number of blocks in message + **************************************************************************************************/ +pub fn native_bech32( + gas_params: &FromBytesGasParameters, + _context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 1); + + let data = pop_arg!(args, VectorRef); + + let cost = + gas_params.base + (gas_params.per_byte * NumBytes::new(data.as_bytes_ref().len() as u64)); + + let binding = data.as_bytes_ref(); + let data_str = std::str::from_utf8(&binding).unwrap(); + + let (hrp, public_key, variant) = bech32::decode(data_str).unwrap(); + + if hrp != "bech32" && hrp != "bech32m" { + return Ok(NativeResult::err(cost, E_DECODE_FAILED)); + }; + + if variant != Variant::Bech32 && variant != Variant::Bech32m { + return Ok(NativeResult::err(cost, E_DECODE_FAILED)); + } + + // TODO redact public length to 20 and 32 bytes + if public_key.len() != 32 && public_key.len() != 52 { + return Ok(NativeResult::err(cost, E_DECODE_FAILED)); + } + + Ok(NativeResult::ok( + cost, + smallvec![Value::vector_u8(public_key.into_iter().map(u8::from))], + )) +} + #[derive(Debug, Clone)] pub struct FromBytesGasParameters { pub base: InternalGas, @@ -108,6 +154,7 @@ impl FromBytesGasParameters { pub struct GasParameters { pub base58: FromBytesGasParameters, pub base58check: FromBytesGasParameters, + pub bech32: FromBytesGasParameters, } impl GasParameters { @@ -115,6 +162,7 @@ impl GasParameters { Self { base58: FromBytesGasParameters::zeros(), base58check: FromBytesGasParameters::zeros(), + bech32: FromBytesGasParameters::zeros(), } } } @@ -126,6 +174,7 @@ pub fn make_all(gas_params: GasParameters) -> impl Iterator): vector` - * gas cost: encoding_p2pkh_cost_base | base cost for function call and fixed opers - * + encoding_p2pkh_data_cost_per_byte * msg.len() | cost depends on length of message - * + encoding_p2pkh_data_cost_per_block * num_blocks | cost depends on number of blocks in message - **************************************************************************************************/ -pub fn native_p2pkh( - gas_params: &FromBytesGasParameters, - _context: &mut NativeContext, - ty_args: Vec, - mut args: VecDeque, -) -> PartialVMResult { - debug_assert!(ty_args.is_empty()); - debug_assert!(args.len() == 1); - - let public_key = pop_arg!(args, VectorRef); - let public_key_bytes_ref = public_key.as_bytes_ref(); - - let cost = - gas_params.base + (gas_params.per_byte * NumBytes::new(public_key_bytes_ref.len() as u64)); - - let Ok(bitcoin_public_key) = PublicKey::from_slice(&public_key_bytes_ref) else { - return Ok(NativeResult::err(cost, E_INVALID_PUBKEY)); - }; - - // Generate the P2PKH address from the bitcoin public key - let p2pkh_address = Address::p2pkh(&bitcoin_public_key, Network::Bitcoin); // TODO network selection - let p2pkh_address_bytes = p2pkh_address.to_string().as_bytes().to_vec(); - - Ok(NativeResult::ok( - cost, - smallvec![Value::vector_u8(p2pkh_address_bytes)], - )) -} - -/*************************************************************************************************** - * native fun p2sh - * Implementation of the Move native function `encoding::p2sh(public_key: &vector): vector` - * gas cost: encoding_p2sh_cost_base | base cost for function call and fixed opers - * + encoding_p2sh_data_cost_per_byte * msg.len() | cost depends on length of message - * + encoding_p2sh_data_cost_per_block * num_blocks | cost depends on number of blocks in message - **************************************************************************************************/ -pub fn native_p2sh( - gas_params: &FromBytesGasParameters, - _context: &mut NativeContext, - ty_args: Vec, - mut args: VecDeque, -) -> PartialVMResult { - debug_assert!(ty_args.is_empty()); - debug_assert!(args.len() == 1); - - let public_key = pop_arg!(args, VectorRef); - let public_key_bytes_ref = public_key.as_bytes_ref(); - - let cost = - gas_params.base + (gas_params.per_byte * NumBytes::new(public_key_bytes_ref.len() as u64)); - - let Ok(bitcoin_public_key) = PublicKey::from_slice(&public_key_bytes_ref) else { - return Ok(NativeResult::err(cost, E_INVALID_PUBKEY)); - }; - - // Create a redeem script (e.g., P2PKH) - let script_pubkey = Address::p2pkh(&bitcoin_public_key, Network::Bitcoin).script_pubkey(); // TODO network selection - let redeem_script = script_pubkey.as_script(); - // Generate the P2SH address from the redeem script - let Ok(p2sh_address) = Address::p2sh( - redeem_script, - Network::Bitcoin, // TODO network selection - ) else { - return Ok(NativeResult::err(cost, E_EXCESSIVE_SCRIPT_SIZE)); - }; - let p2sh_address_bytes = p2sh_address.to_string().as_bytes().to_vec(); + let encoded = + bech32::encode(hrp, public_key.as_bytes_ref().to_vec().to_base32(), variant).unwrap(); Ok(NativeResult::ok( cost, - smallvec![Value::vector_u8(p2sh_address_bytes)], + smallvec![Value::vector_u8(encoded.bytes())], )) } @@ -231,8 +142,6 @@ pub struct GasParameters { pub base58: FromBytesGasParameters, pub base58check: FromBytesGasParameters, pub bech32: FromBytesGasParameters, - pub p2pkh: FromBytesGasParameters, - pub p2sh: FromBytesGasParameters, } impl GasParameters { @@ -241,8 +150,6 @@ impl GasParameters { base58: FromBytesGasParameters::zeros(), base58check: FromBytesGasParameters::zeros(), bech32: FromBytesGasParameters::zeros(), - p2pkh: FromBytesGasParameters::zeros(), - p2sh: FromBytesGasParameters::zeros(), } } } @@ -255,8 +162,6 @@ pub fn make_all(gas_params: GasParameters) -> impl Iterator Result { let payload_type = BitcoinAddressPayloadType::try_from(self.bytes[0])?; match payload_type { @@ -523,13 +524,13 @@ impl BitcoinAddress { let mut prefixed = [0; 21]; prefixed[0] = Self::get_pubkey_address_prefix(network); prefixed[1..].copy_from_slice(&self.bytes[1..]); - Ok(base58::encode_check(&prefixed[..])) + Ok(bs58::encode(&prefixed[..]).with_check().into_string()) } BitcoinAddressPayloadType::ScriptHash => { let mut prefixed = [0; 21]; prefixed[0] = Self::get_script_address_prefix(network); prefixed[1..].copy_from_slice(&self.bytes[1..]); - Ok(base58::encode_check(&prefixed[..])) + Ok(bs58::encode(&prefixed[..]).with_check().into_string()) } BitcoinAddressPayloadType::WitnessProgram => { let hrp = network::Network::try_from(network)?.bech32_hrp(); diff --git a/docs/website/pages/docs/user-guides/hash.en-US.mdx b/docs/website/pages/docs/user-guides/hash.en-US.mdx index e06aadca8c..4510bad0e1 100644 --- a/docs/website/pages/docs/user-guides/hash.en-US.mdx +++ b/docs/website/pages/docs/user-guides/hash.en-US.mdx @@ -40,10 +40,10 @@ There are three main properties that make up a hash function. 2. Second Pre-image Resistance, that is, given an input and its hash value, it is impossible to find a new input different from the input, so that the two inputs produce the same hash value. -For example, in the above example, no matter who creates the empty hello_world.txt file, the digest of the same hash function will be the same. Once the input is slightly modified, the input must be different. +For example, in the above example, no matter who creates the empty hello_world.txt file, the digest of the same hash function will be the same. Once the input is slightly modified, the digest must be different. 3. Collision Resistance, this property ensures that two different inputs with the same hash value cannot be found. -This document mainly introduces some Hash libraries and methods used by Rooch. +This document mainly introduces some hash libraries and methods used by Rooch. ## Public and private keys @@ -64,8 +64,6 @@ These one-way hash functions provide the most basic and important guarantee for - base58 - base58check - bech32 -- p2sh -- p2pkh - ecdsa_k1 - ed25519 diff --git a/docs/website/pages/docs/user-guides/hash.zh-CN.mdx b/docs/website/pages/docs/user-guides/hash.zh-CN.mdx index 78828b618d..40da32b153 100644 --- a/docs/website/pages/docs/user-guides/hash.zh-CN.mdx +++ b/docs/website/pages/docs/user-guides/hash.zh-CN.mdx @@ -65,9 +65,7 @@ SHA2-256(hello_world.txt)= f590572de3c69705a0cad30159ab9f728d28f118d108f7a9b5d02 - base58 - base58check - bech32 -- p2sh -- p2pkh - ecdsa_k1 - ed25519 -更详细的内容,请残阅源码[https://github.com/rooch-network/rooch/tree/main/crates/rooch-framework/sources/crypto](https://github.com/rooch-network/rooch/tree/main/crates/rooch-framework/sources/crypto)。 +更详细的内容,请参阅源码[https://github.com/rooch-network/rooch/tree/main/crates/rooch-framework/sources/crypto](https://github.com/rooch-network/rooch/tree/main/crates/rooch-framework/sources/crypto)。 diff --git a/frameworks/bitcoin-move/Cargo.toml b/frameworks/bitcoin-move/Cargo.toml index 2d907c9e11..103b64468f 100644 --- a/frameworks/bitcoin-move/Cargo.toml +++ b/frameworks/bitcoin-move/Cargo.toml @@ -28,7 +28,6 @@ sha3 = { workspace = true } smallvec = { workspace = true } hex = { workspace = true } bitcoin = { workspace = true } -bitcoin-bech32 = { workspace = true } bs58 = { workspace = true, features = ["check"] } http = { workspace = true } tracing = { workspace = true }