From 2185accb5ec13a00e1b4f461a83d439acb1f8063 Mon Sep 17 00:00:00 2001 From: Milosz Muszynski Date: Wed, 24 Jul 2024 12:55:27 +0200 Subject: [PATCH 1/2] execution-core: added nonce to contract deploy --- execution-core/CHANGELOG.md | 6 ++++++ execution-core/src/transfer.rs | 7 +++++++ execution-core/src/transfer/transaction/moonlight.rs | 1 + execution-core/src/transfer/transaction/phoenix.rs | 1 + execution-core/tests/serialization.rs | 6 ++++++ 5 files changed, 21 insertions(+) diff --git a/execution-core/CHANGELOG.md b/execution-core/CHANGELOG.md index 98f37f0d07..b0d7d6a0cf 100644 --- a/execution-core/CHANGELOG.md +++ b/execution-core/CHANGELOG.md @@ -81,8 +81,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 next_epoch, }` +### Added + +- Add `nonce` contract deploy transaction [#1884] + + [#1963]: https://github.com/dusk-network/rusk/issues/1963 [#1963]: https://github.com/dusk-network/rusk/issues/1856 +[#1884]: https://github.com/dusk-network/rusk/issues/1884 [#1882]: https://github.com/dusk-network/rusk/issues/1882 [#1723]: https://github.com/dusk-network/rusk/issues/1723 diff --git a/execution-core/src/transfer.rs b/execution-core/src/transfer.rs index db021f408c..5d904e0c8f 100644 --- a/execution-core/src/transfer.rs +++ b/execution-core/src/transfer.rs @@ -328,6 +328,8 @@ pub struct ContractDeploy { pub owner: Vec, /// Constructor arguments of the deployed contract. pub constructor_args: Option>, + /// Nonce for contract id uniqueness and vanity + pub nonce: u64, } /// All the data the transfer-contract needs to perform a contract-call. @@ -365,6 +367,8 @@ impl ContractDeploy { None => bytes.push(0), } + bytes.extend(self.nonce.to_bytes()); + bytes } @@ -385,10 +389,13 @@ impl ContractDeploy { _ => return Err(BytesError::InvalidData), }; + let nonce = u64::from_reader(&mut buf)?; + Ok(Self { bytecode, owner, constructor_args, + nonce, }) } } diff --git a/execution-core/src/transfer/transaction/moonlight.rs b/execution-core/src/transfer/transaction/moonlight.rs index dac7b49867..0a9e1bf7fc 100644 --- a/execution-core/src/transfer/transaction/moonlight.rs +++ b/execution-core/src/transfer/transaction/moonlight.rs @@ -91,6 +91,7 @@ impl Transaction { hash: deploy.bytecode.hash, bytes: Vec::new(), }, + nonce: deploy.nonce, })), }, *self.signature(), diff --git a/execution-core/src/transfer/transaction/phoenix.rs b/execution-core/src/transfer/transaction/phoenix.rs index 637f2565aa..4ea2b238e9 100644 --- a/execution-core/src/transfer/transaction/phoenix.rs +++ b/execution-core/src/transfer/transaction/phoenix.rs @@ -97,6 +97,7 @@ impl Transaction { hash: deploy.bytecode.hash, bytes: Vec::new(), }, + nonce: deploy.nonce, })), }, self.proof(), diff --git a/execution-core/tests/serialization.rs b/execution-core/tests/serialization.rs index a178f3050d..8bd6991274 100644 --- a/execution-core/tests/serialization.rs +++ b/execution-core/tests/serialization.rs @@ -165,10 +165,13 @@ fn phoenix_with_deploy() -> Result<(), Error> { let mut constructor_args = vec![0; 20]; rng.fill_bytes(&mut constructor_args); + let nonce = rng.next_u64(); + let deploy = ContractDeploy { bytecode, owner, constructor_args: Some(constructor_args), + nonce, }; let transaction = @@ -240,10 +243,13 @@ fn moonlight_with_deploy() -> Result<(), Error> { let mut constructor_args = vec![0; 20]; rng.fill_bytes(&mut constructor_args); + let nonce = rng.next_u64(); + let deploy = ContractDeploy { bytecode, owner, constructor_args: Some(constructor_args), + nonce, }; let transaction = From 9e9e7800b52a7ccafdd2eb6e48b87049a1aeddee Mon Sep 17 00:00:00 2001 From: Milosz Muszynski Date: Wed, 24 Jul 2024 12:59:13 +0200 Subject: [PATCH 2/2] rusk: passing nonce and owner for contract id generation --- rusk/CHANGELOG.md | 2 + rusk/Cargo.toml | 1 + rusk/src/lib/chain/rusk.rs | 7 ++- rusk/src/lib/gen_id.rs | 53 ++++++++++++++++++++++ rusk/src/lib/hash.rs | 44 ++++++++++++++++++ rusk/src/lib/lib.rs | 2 + rusk/tests/services/contract_deployment.rs | 11 ++++- 7 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 rusk/src/lib/gen_id.rs create mode 100644 rusk/src/lib/hash.rs diff --git a/rusk/CHANGELOG.md b/rusk/CHANGELOG.md index f5f6568727..9f199491be 100644 --- a/rusk/CHANGELOG.md +++ b/rusk/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Add `gen_contract_id` and 32-byte hash for contract deployment [#1884] - Add execution of contract deployment [#1882] - Add first version of RUES, allowing websocket clients to subscribe for events emitted by block transitions [#931] @@ -210,6 +211,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add linking between Rusk and Protobuff structs - Add build system that generates keys for circuits and caches them. +[#1884]: https://github.com/dusk-network/rusk/issues/1884 [#1882]: https://github.com/dusk-network/rusk/issues/1882 [#1675]: https://github.com/dusk-network/rusk/issues/1675 [#1640]: https://github.com/dusk-network/rusk/issues/1640 diff --git a/rusk/Cargo.toml b/rusk/Cargo.toml index 05d0275e52..d2d5b6b83e 100644 --- a/rusk/Cargo.toml +++ b/rusk/Cargo.toml @@ -39,6 +39,7 @@ rkyv = { version = "0.7", default-features = false, features = ["size_32"] } bytecheck = { version = "0.6", default-features = false } dirs = "4" blake3 = "1" +blake2b_simd = { version = "1", default-features = false } poseidon-merkle = { version = "0.6", features = ["rkyv-impl", "size_32"] } sha3 = "0.10" diff --git a/rusk/src/lib/chain/rusk.rs b/rusk/src/lib/chain/rusk.rs index 6ec6fea890..bf6a7b68da 100644 --- a/rusk/src/lib/chain/rusk.rs +++ b/rusk/src/lib/chain/rusk.rs @@ -41,6 +41,7 @@ use rusk_profile::to_rusk_state_id_path; use tokio::sync::broadcast; use super::{coinbase_value, Rusk, RuskTip}; +use crate::gen_id::gen_contract_id; use crate::http::RuesEvent; use crate::Error::InvalidCreditsCount; use crate::{Error, Result}; @@ -540,7 +541,11 @@ fn contract_deploy( receipt.data = Err(Panic("failed bytecode hash check".into())) } else { let result = session.deploy_raw( - None, + Some(gen_contract_id( + &deploy.bytecode.bytes, + deploy.nonce, + &deploy.owner, + )), deploy.bytecode.bytes.as_slice(), deploy.constructor_args.clone(), deploy.owner.clone(), diff --git a/rusk/src/lib/gen_id.rs b/rusk/src/lib/gen_id.rs new file mode 100644 index 0000000000..bb7f2d6389 --- /dev/null +++ b/rusk/src/lib/gen_id.rs @@ -0,0 +1,53 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +use crate::hash::Hasher; +use rusk_abi::ContractId; + +/// Generate a [`ContractId`] address from: +/// - slice of bytes, +/// - nonce +/// - owner +pub fn gen_contract_id( + bytes: impl AsRef<[u8]>, + nonce: u64, + owner: impl AsRef<[u8]>, +) -> ContractId { + let mut hasher = Hasher::new(); + hasher.update(bytes.as_ref()); + hasher.update(nonce.to_le_bytes()); + hasher.update(owner.as_ref()); + let hash_bytes = hasher.finalize(); + ContractId::from_bytes(hash_bytes) +} + +#[cfg(test)] +mod tests { + use super::*; + use rand::rngs::StdRng; + use rand::{RngCore, SeedableRng}; + + #[test] + fn test_gen_contract_id() { + let mut rng = StdRng::seed_from_u64(42); + + let mut bytes = vec![0; 1000]; + rng.fill_bytes(&mut bytes); + + let nonce = rng.next_u64(); + + let mut owner = vec![0, 100]; + rng.fill_bytes(&mut owner); + + let contract_id = + gen_contract_id(bytes.as_slice(), nonce, owner.as_slice()); + + assert_eq!( + hex::encode(contract_id.as_bytes()), + "a138d3b9c87235dac6f62d1d30b75cffbb94601d9cbe5bd540b3e1e5842c8a7d" + ); + } +} diff --git a/rusk/src/lib/hash.rs b/rusk/src/lib/hash.rs new file mode 100644 index 0000000000..523baf6e9b --- /dev/null +++ b/rusk/src/lib/hash.rs @@ -0,0 +1,44 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +use blake2b_simd::{Params, State}; + +/// Hashes scalars and arbitrary slices of bytes using Blake2b, returning an +/// array of 32 bytes. +/// +/// This hash cannot be proven inside a circuit, if that is desired, use +/// `poseidon_hash` instead. +pub struct Hasher { + state: State, +} + +impl Default for Hasher { + fn default() -> Self { + Hasher { + state: Params::new().hash_length(64).to_state(), + } + } +} + +impl Hasher { + /// Create new hasher instance. + pub fn new() -> Self { + Self::default() + } + + /// Process data, updating the internal state. + pub fn update(&mut self, data: impl AsRef<[u8]>) { + self.state.update(data.as_ref()); + } + + /// Retrieve result and consume hasher instance. + pub fn finalize(self) -> [u8; 32] { + let hash = self.state.finalize(); + let mut a = [0u8; 32]; + a.clone_from_slice(&hash.as_array()[..32]); + a + } +} diff --git a/rusk/src/lib/lib.rs b/rusk/src/lib/lib.rs index a3f4edec4c..a9cdd23505 100644 --- a/rusk/src/lib/lib.rs +++ b/rusk/src/lib/lib.rs @@ -9,6 +9,8 @@ #[cfg(feature = "node")] pub mod chain; mod error; +pub mod gen_id; +mod hash; pub mod http; pub mod verifier; mod version; diff --git a/rusk/tests/services/contract_deployment.rs b/rusk/tests/services/contract_deployment.rs index 6294725eb5..ef79774b71 100644 --- a/rusk/tests/services/contract_deployment.rs +++ b/rusk/tests/services/contract_deployment.rs @@ -12,6 +12,7 @@ use execution_core::bytecode::Bytecode; use execution_core::transfer::{ContractDeploy, ContractExec}; use rand::prelude::*; use rand::rngs::StdRng; +use rusk::gen_id::gen_contract_id; use rusk::{Result, Rusk}; use rusk_abi::{ContractData, ContractId, PiecrustError}; use rusk_recovery_tools::state; @@ -77,7 +78,12 @@ fn initial_state>(dir: P, deploy_bob: bool) -> Result { bob_bytecode, ContractData::builder() .owner(OWNER) - .constructor_arg(&BOB_INIT_VALUE), + .constructor_arg(&BOB_INIT_VALUE) + .contract_id(gen_contract_id( + &bob_bytecode, + 0u64, + &OWNER, + )), POINT_LIMIT, ) .expect("Deploying the alice contract should succeed"); @@ -121,6 +127,7 @@ fn make_and_execute_transaction_deploy( }, owner: OWNER.to_vec(), constructor_args, + nonce: 0, }), SENDER_INDEX, gas_limit, @@ -191,7 +198,7 @@ impl Fixture { "../../../target/dusk/wasm32-unknown-unknown/release/bob.wasm" ) .to_vec(); - let contract_id = bytecode_hash(bob_bytecode.as_slice()); + let contract_id = gen_contract_id(&bob_bytecode, 0u64, &OWNER); let path = tmp.into_path(); Self {