diff --git a/execution-core/src/bytecode.rs b/execution-core/src/bytecode.rs new file mode 100644 index 0000000000..4f3f59b5b4 --- /dev/null +++ b/execution-core/src/bytecode.rs @@ -0,0 +1,59 @@ +// 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. + +//! Wrapper for a long data that we want to keep the integrity of. + +extern crate alloc; +use alloc::vec::Vec; +use bytecheck::CheckBytes; +use dusk_bytes::Error as BytesError; +use dusk_bytes::Error::InvalidData; +use rkyv::{Archive, Deserialize, Serialize}; + +#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)] +#[archive_attr(derive(CheckBytes))] +/// Holds bytes of bytecode and its hash. +pub struct Bytecode { + /// Hash of the bytecode bytes. + pub hash: [u8; 32], + /// Bytecode bytes. + pub bytes: Vec, +} + +impl Bytecode { + /// Provides contribution bytes for an external hash. + #[must_use] + pub fn to_hash_input_bytes(&self) -> Vec { + self.hash.to_vec() + } + + /// Serializes this object into a variable length buffer + #[must_use] + pub fn to_var_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.extend(self.hash); + bytes + } + + /// Deserialize from a bytes buffer. + /// + /// # Errors + /// Errors when the bytes are not available. + pub fn from_buf(buf: &[u8]) -> Result<(Self, usize), BytesError> { + if buf.len() < 32 { + return Err(InvalidData); + } + let mut hash = [0u8; 32]; + hash.copy_from_slice(&buf[..32]); + Ok(( + Self { + hash, + bytes: Vec::new(), + }, + 32, + )) + } +} diff --git a/execution-core/src/lib.rs b/execution-core/src/lib.rs index ce60bf3dcb..83ee772c5d 100644 --- a/execution-core/src/lib.rs +++ b/execution-core/src/lib.rs @@ -13,6 +13,7 @@ /// Block height type alias pub type BlockHeight = u64; +pub mod bytecode; pub mod stake; pub mod transfer; diff --git a/execution-core/src/transfer.rs b/execution-core/src/transfer.rs index 77bdf30582..1ac6b5d483 100644 --- a/execution-core/src/transfer.rs +++ b/execution-core/src/transfer.rs @@ -29,6 +29,7 @@ use crate::{ }; mod transaction; +use crate::bytecode::Bytecode; pub use transaction::{Payload, Transaction}; /// Unique ID to identify a contract. @@ -110,7 +111,7 @@ pub struct ContractDeploy { /// The optional ID of the contract to be deployed. pub contract_id: Option, /// Bytecode of the contract to be deployed. - pub bytecode: Vec, + pub bytecode: Bytecode, /// Owner of the contract to be deployed. pub owner: Vec, /// Constructor arguments of the deployed contract. @@ -146,8 +147,7 @@ impl ContractDeploy { None => bytes.push(0), } - bytes.extend((self.bytecode.len() as u64).to_bytes()); - bytes.extend(&self.bytecode); + bytes.extend(&self.bytecode.to_var_bytes()); bytes.extend((self.owner.len() as u64).to_bytes()); bytes.extend(&self.owner); @@ -183,9 +183,7 @@ impl ContractDeploy { }; let mut buf = &buf[offset..]; - let bytecode_len = usize::try_from(u64::from_reader(&mut buf)?) - .map_err(|_| BytesError::InvalidData)?; - let bytecode = buf[..bytecode_len].into(); + let (bytecode, bytecode_len) = Bytecode::from_buf(buf)?; buf = &buf[bytecode_len..]; let owner_len = usize::try_from(u64::from_reader(&mut buf)?) diff --git a/execution-core/src/transfer/transaction.rs b/execution-core/src/transfer/transaction.rs index 3d113a51b5..2a2ad06f48 100644 --- a/execution-core/src/transfer/transaction.rs +++ b/execution-core/src/transfer/transaction.rs @@ -139,7 +139,7 @@ impl Payload { if let Some(contract_id) = &deploy.contract_id { bytes.extend(contract_id); } - bytes.extend(&deploy.bytecode); + bytes.extend(&deploy.bytecode.to_hash_input_bytes()); bytes.extend(&deploy.owner); if let Some(constructor_args) = &deploy.constructor_args { bytes.extend(constructor_args); @@ -161,8 +161,10 @@ impl Payload { #[derive(Debug, Clone, Archive, Serialize, Deserialize)] #[archive_attr(derive(CheckBytes))] pub struct Transaction { - payload: Payload, - proof: Vec, + /// Payload + pub payload: Payload, + /// Proof + pub proof: Vec, } impl PartialEq for Transaction { diff --git a/execution-core/tests/serialization.rs b/execution-core/tests/serialization.rs index 593fad0345..0fc0173dad 100644 --- a/execution-core/tests/serialization.rs +++ b/execution-core/tests/serialization.rs @@ -7,6 +7,7 @@ use dusk_bls12_381::BlsScalar; use dusk_bytes::{Error, Serializable}; use dusk_jubjub::JubJubScalar; +use execution_core::bytecode::Bytecode; use execution_core::transfer::{ CallOrDeploy, ContractCall, ContractDeploy, Fee, Payload, Transaction, }; @@ -95,13 +96,27 @@ fn transaction_serialization_call() -> Result<(), Error> { Ok(()) } +fn strip_off_bytecode(tx: &Transaction) -> Transaction { + let mut tx_clone = tx.clone(); + match &mut tx_clone.payload.call_or_deploy { + Some(CallOrDeploy::Deploy(deploy)) => { + deploy.bytecode.bytes.clear(); + } + _ => (), + } + tx_clone +} + #[test] fn transaction_serialization_deploy() -> Result<(), Error> { let (tx_skeleton, fee, _) = build_skeleton_fee_deposit(); // build the contract-deploy let contract = [42; 32]; - let bytecode = vec![1, 2, 3, 4, 5]; + let bytecode = Bytecode { + hash: [1u8; 32], + bytes: vec![1, 2, 3, 4, 5], + }; let owner = [1; 32]; let constructor_args = vec![5]; let deploy = ContractDeploy { @@ -121,8 +136,14 @@ fn transaction_serialization_deploy() -> Result<(), Error> { // set a random proof let proof = [42; 42].to_vec(); - let transaction = Transaction::new(payload, proof); + // bytecode not stripped off + let transaction = Transaction::new(payload.clone(), proof.clone()); + let transaction_bytes = transaction.to_var_bytes(); + let deserialized = Transaction::from_slice(&transaction_bytes)?; + assert_eq!(transaction, deserialized); + // bytecode stripped off + let transaction = strip_off_bytecode(&Transaction::new(payload, proof)); let transaction_bytes = transaction.to_var_bytes(); let deserialized = Transaction::from_slice(&transaction_bytes)?; assert_eq!(transaction, deserialized);