Pārlūkot izejas kodu

contract/deployooor: Initial impl of non-native smart contract deployment.

This supports (Re)Deployment of smart contracts, and additionally making
them immutable after they have been deployed.
parazyd 3 gadi atpakaļ
vecāks
revīzija
d59d3cd21a

+ 10 - 0
Cargo.lock

@@ -1229,6 +1229,16 @@ dependencies = [
  "sqlx",
 ]
 
+[[package]]
+name = "darkfi-deployooor-contract"
+version = "0.4.1"
+dependencies = [
+ "darkfi-sdk",
+ "darkfi-serial",
+ "getrandom",
+ "thiserror",
+]
+
 [[package]]
 name = "darkfi-derive"
 version = "0.4.1"

+ 1 - 0
Cargo.toml

@@ -50,6 +50,7 @@ members = [
     "src/contract/money",
     "src/contract/dao",
     "src/contract/consensus",
+    "src/contract/deployooor",
 
     "example/dchat",
 ]

+ 0 - 29
proof/deploy_contract.zk

@@ -1,29 +0,0 @@
-constant "DeployContract" {
-	EcFixedPointBase NULLIFIER_K,
-}
-
-witness "DeployContract" {
-	# Amount of bytes to store on-chain
-	Base bytes,
-	# Deploy key used for signing and contract reference
-	Base deploy_key,
-}
-
-circuit "DeployContract" {
-	# Derive a public key used for the signature and constrain
-	# its coordinates:
-	signature_public = ec_mul_base(deploy_key, NULLIFIER_K);
-	signature_x = ec_get_x(signature_public);
-	signature_y = ec_get_y(signature_public);
-	constrain_instance(signature_x);
-	constrain_instance(signature_y);
-
-	# Derive the contract address from the public key's coordinates
-	address = poseidon_hash(signature_x, signature_y);
-	constrain_instance(address);
-
-	# Constrain the byte size of the deployed binaries
-	constrain_instance(bytes);
-
-	# TODO: Fee cost for storing this data on-chain
-}

+ 5 - 2
src/contract/README.md

@@ -1,10 +1,9 @@
-This directory contains native WASM contracts on DarkFi.
+This directory contains native WASM contracts on DarkFi:
 
 ## Money
 
 * https://darkrenaissance.github.io/darkfi/development/darkfi_money_contract/index.html
 
-
 ## DAO
 
 * https://darkrenaissance.github.io/darkfi/development/darkfi_dao_contract/index.html
@@ -12,3 +11,7 @@ This directory contains native WASM contracts on DarkFi.
 ## Consensus
 
 * https://darkrenaissance.github.io/darkfi/development/darkfi_consensus_contract/index.html
+
+## Deployooor
+
+* https://darkrenaissance.github.io/darkfi/development/darkfi_deployooor_contract/index.html

+ 1 - 0
src/contract/deployooor/.gitignore

@@ -0,0 +1 @@
+deployooor_contract.wasm

+ 23 - 0
src/contract/deployooor/Cargo.toml

@@ -0,0 +1,23 @@
+[package]
+name = "darkfi-deployooor-contract"
+version = "0.4.1"
+authors = ["Dyne.org foundation <foundation@dyne.org>"]
+license = "AGPL-3.0-only"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "rlib"]
+
+[dependencies]
+darkfi-sdk = { path = "../../sdk" }
+darkfi-serial = { path = "../../serial", features = ["derive", "crypto"] }
+thiserror = "1.0.40"
+
+# We need to disable random using "custom" which makes the crate a noop
+# so the wasm32-unknown-unknown target is enabled.
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+getrandom = { version = "0.2.8", features = ["custom"] }
+
+[features]
+default = []
+no-entrypoint = []

+ 34 - 0
src/contract/deployooor/Makefile

@@ -0,0 +1,34 @@
+.POSIX:
+
+# Cargo binary
+CARGO = cargo
+
+# zkas compiler binary
+ZKAS = ../../../zkas
+
+# zkas circuits
+PROOFS_SRC = $(shell find proof -type f -name '*.zk')
+PROOFS_BIN = $(PROOFS_SRC:=.bin)
+
+# wasm source files
+WASM_SRC = \
+	$(shell find src -type f) \
+	$(shell find ../../sdk -type f) \
+	$(shell find ../../serial -type f)
+
+# wasm contract binary
+WASM_BIN = deployooor_contract.wasm
+
+all: $(WASM_BIN)
+
+$(WASM_BIN): $(WASM_SRC) $(PROOFS_BIN)
+	$(CARGO) build --release --package darkfi-deployooor-contract --target wasm32-unknown-unknown
+	cp -f ../../../target/wasm32-unknown-unknown/release/darkfi_deployooor_contract.wasm $@
+
+$(PROOFS_BIN): $(ZKAS) $(PROOFS_SRC)
+	$(ZKAS) $(basename $@) -o $@
+
+clean:
+	rm -f $(PROOFS_BIN) $(WASM_BIN)
+
+.PHONY: all clean

+ 24 - 0
src/contract/deployooor/proof/derive_contract_id.zk

@@ -0,0 +1,24 @@
+constant "DeriveContractID" {
+	EcFixedPointBase NULLIFIER_K,
+}
+
+witness "DeriveContractID" {
+	# Deploy key used for signing and contract ID derivation
+	Base deploy_key,
+}
+
+circuit "DeriveContractID" {
+	# ContractID derivation path (See darkfi_sdk::crypto::ContractId)
+	derivation_path = witness_base(42);
+
+	# Derive the public key used for the signature
+	signature_public = ec_mul_base(deploy_key, NULLIFIER_K);
+	signature_x = ec_get_x(signature_public);
+	signature_y = ec_get_y(signature_public);
+	constrain_instance(signature_x);
+	constrain_instance(signature_y);
+
+	# Derive the Contract ID
+	contract_id = poseidon_hash(derivation_path, signature_x, signature_y);
+	constrain_instance(contract_id);
+}

+ 144 - 0
src/contract/deployooor/src/entrypoint.rs

@@ -0,0 +1,144 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use darkfi_sdk::{
+    crypto::ContractId,
+    db::{db_init, db_lookup, db_set, zkas_db_set},
+    error::{ContractError, ContractResult},
+    msg,
+    util::set_return_data,
+    ContractCall,
+};
+use darkfi_serial::{deserialize, serialize};
+
+use crate::{
+    model::{DeployUpdateV1, LockUpdateV1},
+    DeployFunction, DEPLOY_CONTRACT_DB_VERSION, DEPLOY_CONTRACT_INFO_TREE,
+    DEPLOY_CONTRACT_LOCK_TREE,
+};
+
+/// `Deployooor::Deploy` functions
+mod deploy_v1;
+use deploy_v1::{deploy_get_metadata_v1, deploy_process_instruction_v1, deploy_process_update_v1};
+
+/// `Deployooor::Lock` functions
+mod lock_v1;
+use lock_v1::{lock_get_metadata_v1, lock_process_instruction_v1, lock_process_update_v1};
+
+darkfi_sdk::define_contract!(
+    init: init_contract,
+    exec: process_instruction,
+    apply: process_update,
+    metadata: get_metadata
+);
+
+/// This entrypoint function runs when the contract is (re)deployed and initialized.
+/// We use this function to initialize all the necessary databases and prepare them
+/// with initial data if necessary.
+fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
+    // Set up the zkas circuit tree
+    let derive_cid_bincode = include_bytes!("../proof/derive_contract_id.zk");
+    zkas_db_set(&derive_cid_bincode[..])?;
+
+    // Set up a database tree for arbitrary data
+    let info_db = match db_lookup(cid, DEPLOY_CONTRACT_INFO_TREE) {
+        Ok(v) => v,
+        Err(_) => {
+            let info_db = db_init(cid, DEPLOY_CONTRACT_INFO_TREE)?;
+            info_db
+        }
+    };
+
+    // Set up a database to hold the set of locked contracts
+    // k=ContractId, v=bool
+    if db_lookup(cid, DEPLOY_CONTRACT_LOCK_TREE).is_err() {
+        db_init(cid, DEPLOY_CONTRACT_LOCK_TREE)?;
+    }
+
+    // Update db version
+    db_set(
+        info_db,
+        &serialize(&DEPLOY_CONTRACT_DB_VERSION),
+        &serialize(&env!("CARGO_PKG_VERSION")),
+    )?;
+
+    Ok(())
+}
+
+/// This function is used by the wasm VM's host to fetch the necessary metadata
+/// for verifying signatures and zk proofs. The payload given here are all the
+/// contract calls in the transaction.
+fn get_metadata(cid: ContractId, ix: &[u8]) -> ContractResult {
+    let (call_idx, calls): (u32, Vec<ContractCall>) = deserialize(ix)?;
+    if call_idx >= calls.len() as u32 {
+        msg!("Error: call_idx >= calls.len()");
+        return Err(ContractError::Internal)
+    }
+
+    match DeployFunction::try_from(calls[call_idx as usize].data[0])? {
+        DeployFunction::DeployV1 => {
+            let metadata = deploy_get_metadata_v1(cid, call_idx, calls)?;
+            Ok(set_return_data(&metadata)?)
+        }
+
+        DeployFunction::LockV1 => {
+            let metadata = lock_get_metadata_v1(cid, call_idx, calls)?;
+            Ok(set_return_data(&metadata)?)
+        }
+    }
+}
+
+/// This function verifies a state transition and produces a state update
+/// if everything is successful.
+fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
+    let (call_idx, calls): (u32, Vec<ContractCall>) = deserialize(ix)?;
+    if call_idx >= calls.len() as u32 {
+        msg!("Error: call_idx >= calls.len()");
+        return Err(ContractError::Internal)
+    }
+
+    match DeployFunction::try_from(calls[call_idx as usize].data[0])? {
+        DeployFunction::DeployV1 => {
+            let update_data = deploy_process_instruction_v1(cid, call_idx, calls)?;
+            Ok(set_return_data(&update_data)?)
+        }
+
+        DeployFunction::LockV1 => {
+            let update_data = lock_process_instruction_v1(cid, call_idx, calls)?;
+            Ok(set_return_data(&update_data)?)
+        }
+    }
+}
+
+/// This function attempts to write a given state update provided the previous
+/// steps of the contract call execution were all successful. It's the last in
+/// line, and assumes that the transaction/call was successful. The payload
+/// given to the function is the update data retrieved from `process_instruction()`.
+fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
+    match DeployFunction::try_from(update_data[0])? {
+        DeployFunction::DeployV1 => {
+            let update: DeployUpdateV1 = deserialize(&update_data[1..])?;
+            Ok(deploy_process_update_v1(cid, update)?)
+        }
+
+        DeployFunction::LockV1 => {
+            let update: LockUpdateV1 = deserialize(&update_data[1..])?;
+            Ok(lock_process_update_v1(cid, update)?)
+        }
+    }
+}

+ 103 - 0
src/contract/deployooor/src/entrypoint/deploy_v1.rs

@@ -0,0 +1,103 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use darkfi_sdk::{
+    crypto::{ContractId, PublicKey},
+    db::{db_get, db_lookup, db_set},
+    error::{ContractError, ContractResult},
+    msg,
+    pasta::pallas,
+    ContractCall,
+};
+use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
+
+use crate::{
+    error::DeployError,
+    model::{DeployParamsV1, DeployUpdateV1},
+    DeployFunction, DEPLOY_CONTRACT_LOCK_TREE, DEPLOY_CONTRACT_ZKAS_DERIVE_NS_V1,
+};
+
+/// `get_metadata` function for `Deploy::DeployV1`
+pub(crate) fn deploy_get_metadata_v1(
+    _cid: ContractId,
+    call_idx: u32,
+    calls: Vec<ContractCall>,
+) -> Result<Vec<u8>, ContractError> {
+    let self_ = &calls[call_idx as usize];
+    let params: DeployParamsV1 = deserialize(&self_.data[1..])?;
+
+    // Public inputs for the ZK proofs we have to verify
+    let mut zk_public_inputs: Vec<(String, Vec<pallas::Base>)> = vec![];
+    // Public keys for the transaction signatures we have to verify
+    let signature_pubkeys: Vec<PublicKey> = vec![params.public_key];
+
+    // Derive the ContractID from the public key
+    let (sig_x, sig_y) = params.public_key.xy();
+    let contract_id = ContractId::derive_public(params.public_key);
+
+    // Append the ZK public inputs
+    zk_public_inputs.push((
+        DEPLOY_CONTRACT_ZKAS_DERIVE_NS_V1.to_string(),
+        vec![sig_x, sig_y, contract_id.inner()],
+    ));
+
+    // Serialize everything gathered and return it
+    let mut metadata = vec![];
+    zk_public_inputs.encode(&mut metadata)?;
+    signature_pubkeys.encode(&mut metadata)?;
+
+    Ok(metadata)
+}
+
+/// `process_instruction` function for `Deploy::DeployV1`
+pub(crate) fn deploy_process_instruction_v1(
+    cid: ContractId,
+    call_idx: u32,
+    calls: Vec<ContractCall>,
+) -> Result<Vec<u8>, ContractError> {
+    let self_ = &calls[call_idx as usize];
+    let params: DeployParamsV1 = deserialize(&self_.data[1..])?;
+
+    // In this function, we have to check that the contract isn't locked.
+    let lock_db = db_lookup(cid, DEPLOY_CONTRACT_LOCK_TREE)?;
+    let contract_id = ContractId::derive_public(params.public_key);
+
+    if let Some(v) = db_get(lock_db, &serialize(&contract_id))? {
+        let locked: bool = deserialize(&v)?;
+        if locked {
+            msg!("[DeployV1] Error: Contract is locked. Cannot redeploy.");
+            return Err(DeployError::ContractLocked.into())
+        }
+    }
+
+    let update = DeployUpdateV1 { contract_id };
+    let mut update_data = vec![];
+    update_data.write_u8(DeployFunction::DeployV1 as u8)?;
+    update.encode(&mut update_data)?;
+    Ok(update_data)
+}
+
+/// `process_update` function for `Deploy::DeployV1`
+pub(crate) fn deploy_process_update_v1(cid: ContractId, update: DeployUpdateV1) -> ContractResult {
+    // We add the contract to the list
+    msg!("[DeployV1] Adding ContractID to deployed list");
+    let lock_db = db_lookup(cid, DEPLOY_CONTRACT_LOCK_TREE)?;
+    db_set(lock_db, &serialize(&update.contract_id), &serialize(&false))?;
+
+    Ok(())
+}

+ 109 - 0
src/contract/deployooor/src/entrypoint/lock_v1.rs

@@ -0,0 +1,109 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use darkfi_sdk::{
+    crypto::{ContractId, PublicKey},
+    db::{db_contains_key, db_get, db_lookup, db_set},
+    error::{ContractError, ContractResult},
+    msg,
+    pasta::pallas,
+    ContractCall,
+};
+use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
+
+use crate::{
+    error::DeployError,
+    model::{LockParamsV1, LockUpdateV1},
+    DeployFunction, DEPLOY_CONTRACT_LOCK_TREE, DEPLOY_CONTRACT_ZKAS_DERIVE_NS_V1,
+};
+
+/// `get_metadata` function for `Deploy::LockV1`
+pub(crate) fn lock_get_metadata_v1(
+    _cid: ContractId,
+    call_idx: u32,
+    calls: Vec<ContractCall>,
+) -> Result<Vec<u8>, ContractError> {
+    let self_ = &calls[call_idx as usize];
+    let params: LockParamsV1 = deserialize(&self_.data[1..])?;
+
+    // Public inputs for the ZK proofs we have to verify
+    let mut zk_public_inputs: Vec<(String, Vec<pallas::Base>)> = vec![];
+    // Public keys for the transaction signatures we have to verify
+    let signature_pubkeys: Vec<PublicKey> = vec![params.public_key];
+
+    // Derive the ContractID from the public key
+    let (sig_x, sig_y) = params.public_key.xy();
+    let contract_id = ContractId::derive_public(params.public_key);
+
+    // Append the ZK public inputs
+    zk_public_inputs.push((
+        DEPLOY_CONTRACT_ZKAS_DERIVE_NS_V1.to_string(),
+        vec![sig_x, sig_y, contract_id.inner()],
+    ));
+
+    // Serialize everything gathered and return it
+    let mut metadata = vec![];
+    zk_public_inputs.encode(&mut metadata)?;
+    signature_pubkeys.encode(&mut metadata)?;
+
+    Ok(metadata)
+}
+
+/// `process_instruction` function for `Deploy::LockV1`
+pub(crate) fn lock_process_instruction_v1(
+    cid: ContractId,
+    call_idx: u32,
+    calls: Vec<ContractCall>,
+) -> Result<Vec<u8>, ContractError> {
+    let self_ = &calls[call_idx as usize];
+    let params: LockParamsV1 = deserialize(&self_.data[1..])?;
+
+    // In this function, we check that the contract exists, and that it isn't
+    // already locked.
+    let lock_db = db_lookup(cid, DEPLOY_CONTRACT_LOCK_TREE)?;
+    let contract_id = ContractId::derive_public(params.public_key);
+
+    if !db_contains_key(lock_db, &serialize(&contract_id))? {
+        msg!("[LockV1] Error: Contract ID doesn't exist.");
+        return Err(DeployError::ContractNonExistent.into())
+    }
+
+    let v = db_get(lock_db, &serialize(&contract_id))?.unwrap();
+    let locked: bool = deserialize(&v)?;
+    if locked {
+        msg!("[LockV1] Error: Contract already locked.");
+        return Err(DeployError::ContractLocked.into())
+    }
+
+    let update = LockUpdateV1 { contract_id };
+    let mut update_data = vec![];
+    update_data.write_u8(DeployFunction::LockV1 as u8)?;
+    update.encode(&mut update_data)?;
+
+    Ok(update_data)
+}
+
+/// `process_update` function for `Deploy::LockV1`
+pub(crate) fn lock_process_update_v1(cid: ContractId, update: LockUpdateV1) -> ContractResult {
+    // We make the contract immutable
+    msg!("[LockV1] Making ContractID immutable");
+    let lock_db = db_lookup(cid, DEPLOY_CONTRACT_LOCK_TREE)?;
+    db_set(lock_db, &serialize(&update.contract_id), &serialize(&true))?;
+
+    Ok(())
+}

+ 37 - 0
src/contract/deployooor/src/error.rs

@@ -0,0 +1,37 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use darkfi_sdk::error::ContractError;
+
+#[derive(Debug, Clone, thiserror::Error)]
+pub enum DeployError {
+    #[error("Contract deployment is locked.")]
+    ContractLocked,
+
+    #[error("Contract does not exist.")]
+    ContractNonExistent,
+}
+
+impl From<DeployError> for ContractError {
+    fn from(e: DeployError) -> Self {
+        match e {
+            DeployError::ContractLocked => Self::Custom(1),
+            DeployError::ContractNonExistent => Self::Custom(2),
+        }
+    }
+}

+ 60 - 0
src/contract/deployooor/src/lib.rs

@@ -0,0 +1,60 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//! Smart contract implementing non-native smart contract deployment.
+
+use darkfi_sdk::error::ContractError;
+
+/// Functions available in the contract
+#[repr(u8)]
+pub enum DeployFunction {
+    DeployV1 = 0x00,
+    LockV1 = 0x01,
+}
+
+impl TryFrom<u8> for DeployFunction {
+    type Error = ContractError;
+
+    fn try_from(b: u8) -> core::result::Result<Self, Self::Error> {
+        match b {
+            0x00 => Ok(Self::DeployV1),
+            0x01 => Ok(Self::LockV1),
+            _ => Err(ContractError::InvalidFunction),
+        }
+    }
+}
+
+#[cfg(not(feature = "no-entrypoint"))]
+/// WASM entrypoint functions
+pub mod entrypoint;
+
+/// Call parameters definitions
+pub mod model;
+
+/// Contract errors
+pub mod error;
+
+// These are the different sled trees that will be created
+pub const DEPLOY_CONTRACT_INFO_TREE: &str = "info";
+pub const DEPLOY_CONTRACT_LOCK_TREE: &str = "lock";
+
+// These are keys inside the info tree
+pub const DEPLOY_CONTRACT_DB_VERSION: &str = "db_version";
+
+/// zkas derive circuit namespace
+pub const DEPLOY_CONTRACT_ZKAS_DERIVE_NS_V1: &str = "DeriveContractID";

+ 50 - 0
src/contract/deployooor/src/model.rs

@@ -0,0 +1,50 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use darkfi_sdk::crypto::{ContractId, PublicKey};
+use darkfi_serial::{SerialDecodable, SerialEncodable};
+
+/// Parameters for `Deploy::Deploy`
+#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+pub struct DeployParamsV1 {
+    /// Webassembly bincode of the smart contract
+    pub wasm_bincode: Vec<u8>,
+    /// Public key used to sign the transaction and derive the `ContractId`
+    pub public_key: PublicKey,
+}
+
+/// State update for `Deploy::Deploy`
+#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+pub struct DeployUpdateV1 {
+    /// The `ContractId` to deploy
+    pub contract_id: ContractId,
+}
+
+/// Parameters for `Deploy::Lock`
+#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+pub struct LockParamsV1 {
+    /// Public key used to sign the transaction and derive the `ContractId`
+    pub public_key: PublicKey,
+}
+
+/// State update for `Deploy::Lock`
+#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+pub struct LockUpdateV1 {
+    /// The `ContractId` to lock
+    pub contract_id: ContractId,
+}