From 49ee268e74f6d7f2849da7a5cd6e4f591153d168 Mon Sep 17 00:00:00 2001 From: Cameron Carstens <54727135+bitzoic@users.noreply.github.com> Date: Tue, 5 Sep 2023 13:29:48 +0200 Subject: [PATCH] Implement `Hash` trait for `Bytes` and `String` types (#5089) ## Description The current `Hasher` implementation uses the `sha256()` and `keccak256()` functions provided in the `Bytes` type implementation. This PR moves the assembly block that does the hashing into the `Hasher`. This allows for the homogenous syntax of hashing any type and moves all hashing into the `hash.sw` file. This PR also implements the `Hash` trait for the `Bytes` and `String` types. Before: ```rust assert(sha256(my_string) == my_bytes.sha256()); ``` After: ```rust assert(sha256(my_string) == sha256(my_bytes)); ``` ## Checklist - [ ] I have linked to any relevant issues. - [ ] I have commented my code, particularly in hard-to-understand areas. - [ ] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers. --------- Co-authored-by: bitzoic --- sway-lib-std/src/bytes.sw | 54 ------------------- sway-lib-std/src/hash.sw | 38 +++++++++++-- sway-lib-std/src/string.sw | 21 +++++++- .../configurable_consts/json_abi_oracle.json | 18 +++---- .../call_basic_storage/src/main.sw | 2 +- 5 files changed, 65 insertions(+), 68 deletions(-) diff --git a/sway-lib-std/src/bytes.sw b/sway-lib-std/src/bytes.sw index ae02397c8b9..d6441211b10 100644 --- a/sway-lib-std/src/bytes.sw +++ b/sway-lib-std/src/bytes.sw @@ -537,60 +537,6 @@ impl Bytes { pub fn is_empty(self) -> bool { self.len == 0 } - - /// Returns the `SHA-2-256` hash of the elements. - /// - /// # Returns - /// - /// * [b256] - The `SHA-2-256` hash of the elements. - /// - /// # Examples - /// - /// ```sway - /// use std:bytes::Bytes; - /// - /// fn foo() { - /// let bytes = Bytes::new(); - /// bytes.push(1); - /// bytes.push(2); - /// bytes.push(3); - /// let sha256_hash = bytes.sha256(); - /// } - /// ``` - pub fn sha256(self) -> b256 { - let mut result_buffer = b256::min(); - asm(hash: result_buffer, ptr: self.buf.ptr, bytes: self.len) { - s256 hash ptr bytes; - hash: b256 - } - } - - /// Returns the `KECCAK-256` hash of the elements. - /// - /// # Returns - /// - /// * [b256] - The `KECCAK-256` hash of the elements. - /// - /// # Examples - /// - /// ```sway - /// use std:bytes::Bytes; - /// - /// fn foo() { - /// let bytes = Bytes::new(); - /// bytes.push(1); - /// bytes.push(2); - /// bytes.push(3); - /// let keccak256_hash = bytes.keccak256(); - /// } - /// ``` - pub fn keccak256(self) -> b256 { - let mut result_buffer = b256::min(); - asm(hash: result_buffer, ptr: self.buf.ptr, bytes: self.len) { - k256 hash ptr bytes; - hash: b256 - } - } } // Need to use seperate impl blocks for now: https://github.com/FuelLabs/sway/issues/1548 diff --git a/sway-lib-std/src/hash.sw b/sway-lib-std/src/hash.sw index b83bc094add..2e14e36f2ee 100644 --- a/sway-lib-std/src/hash.sw +++ b/sway-lib-std/src/hash.sw @@ -18,11 +18,19 @@ impl Hasher { } pub fn sha256(self) -> b256 { - self.bytes.sha256() + let mut result_buffer = b256::min(); + asm(hash: result_buffer, ptr: self.bytes.buf.ptr, bytes: self.bytes.len) { + s256 hash ptr bytes; + hash: b256 + } } pub fn keccak256(self) -> b256 { - self.bytes.keccak256() + let mut result_buffer = b256::min(); + asm(hash: result_buffer, ptr: self.bytes.buf.ptr, bytes: self.bytes.len) { + k256 hash ptr bytes; + hash: b256 + } } } @@ -123,6 +131,12 @@ impl Hash for bool { } } +impl Hash for Bytes { + fn hash(self, ref mut state: Hasher) { + state.write(self); + } +} + impl Hash for (A, B) where A: Hash, B: Hash { #![inline(never)] fn hash(self, ref mut state: Hasher) { @@ -418,4 +432,22 @@ fn test_hasher_sha256_bool() { true.hash(hasher); let sha256 = hasher.sha256(); assert(sha256 == 0x4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a); -} \ No newline at end of file +} + +#[test] +fn test_hasher_sha256_bytes() { + use ::assert::assert; + let mut hasher = Hasher::new(); + let mut bytes = Bytes::new(); + bytes.push(0u8); + bytes.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0x6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d); + + let mut hasher = Hasher::new(); + let mut bytes = Bytes::new(); + bytes.push(1u8); + bytes.hash(hasher); + let sha256 = hasher.sha256(); + assert(sha256 == 0x4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a); +} diff --git a/sway-lib-std/src/string.sw b/sway-lib-std/src/string.sw index 67cf6ad8458..358d614995d 100644 --- a/sway-lib-std/src/string.sw +++ b/sway-lib-std/src/string.sw @@ -1,9 +1,10 @@ library; +use ::assert::assert; use ::bytes::Bytes; use ::convert::From; +use ::hash::{Hash, Hasher}; use ::option::Option; -use ::assert::assert; /// A UTF-8 encoded growable string. @@ -265,6 +266,12 @@ impl Eq for String { } } +impl Hash for String { + fn hash(self, ref mut state: Hasher) { + state.write(self.bytes); + } +} + // Tests #[test] @@ -495,3 +502,15 @@ fn string_test_equal() { assert(string1 == string2); assert(string1 != string3); } + +#[test] +fn string_test_hash() { + use ::hash::sha256; + + let mut bytes = Bytes::new(); + bytes.push(0u8); + + let string = String::from(bytes); + + assert(sha256(string) == sha256(bytes)); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.json index 516e194c8ec..867722444ea 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.json @@ -7,7 +7,7 @@ "typeArguments": null }, "name": "C0", - "offset": 7564 + "offset": 7372 }, { "configurableType": { @@ -16,7 +16,7 @@ "typeArguments": null }, "name": "C1", - "offset": 7572 + "offset": 7380 }, { "configurableType": { @@ -25,7 +25,7 @@ "typeArguments": null }, "name": "C2", - "offset": 7588 + "offset": 7396 }, { "configurableType": { @@ -34,7 +34,7 @@ "typeArguments": [] }, "name": "C3", - "offset": 7620 + "offset": 7428 }, { "configurableType": { @@ -43,7 +43,7 @@ "typeArguments": [] }, "name": "C4", - "offset": 7636 + "offset": 7444 }, { "configurableType": { @@ -52,7 +52,7 @@ "typeArguments": [] }, "name": "C5", - "offset": 7652 + "offset": 7460 }, { "configurableType": { @@ -61,7 +61,7 @@ "typeArguments": null }, "name": "C6", - "offset": 7668 + "offset": 7476 }, { "configurableType": { @@ -70,7 +70,7 @@ "typeArguments": null }, "name": "C7", - "offset": 7684 + "offset": 7492 }, { "configurableType": { @@ -79,7 +79,7 @@ "typeArguments": null }, "name": "C9", - "offset": 7732 + "offset": 7540 } ], "functions": [ diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw index 343b90968cb..04f219af81d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw @@ -2,7 +2,7 @@ script; use basic_storage_abi::{BasicStorage, Quad}; fn main() -> u64 { - let addr = abi(BasicStorage, 0x576da79735d7fe54047bef8df46195e2c0978726f68f3c840902b75e77d99c96); + let addr = abi(BasicStorage, 0x21d0b9983dced02ae881b2936f20f69026963f6cc4eb0bfb4991e16b7f8c580f); let key = 0x0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; let value = 4242;