Skip to content

Commit

Permalink
Merge pull request #120 from dusk-network/genesis-types-119
Browse files Browse the repository at this point in the history
Add types to interact with the genesis contracts
  • Loading branch information
Eduardo Leegwater Simões authored Jan 19, 2023
2 parents 2ba8ed2 + 038db3d commit ce27314
Show file tree
Hide file tree
Showing 5 changed files with 426 additions and 50 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## Added

- Add `allow_signature_message`, `stake_signature_message`,
`unstake_signature_message`, and `withdraw_signature_message`
to generate signature messages for stake contract interaction [#119]
- Add `stct_signature_message` and `stco_signature_message` to generate
signature messages for transfer contract interaction [#119]
- Add `Stake`, `Unstake`, `Withdraw`, `Allow`, and `StakeData` structs to allow
interaction with the stake contract [#119]
- Add `Stct`, `Wfct`, `Stco`, `Wfco`, `Wfctc`, `Mint`, and `TreeLeaf` structs to
allow interaction with the transfer contract [#119]
- Add `Transaction` structure [#116]

## [0.18.0] - 2022-11-02

### Added
Expand Down Expand Up @@ -173,6 +186,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Removal of anyhow error implementation.
- Canonical implementation shielded by feature.

[#119]: https://github.com/dusk-network/phoenix-core/issues/119
[#116]: https://github.com/dusk-network/phoenix-core/issues/116
[#114]: https://github.com/dusk-network/phoenix-core/issues/114
[#107]: https://github.com/dusk-network/phoenix-core/issues/107
[#96]: https://github.com/dusk-network/phoenix-core/issues/96
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ exclude = [".github/workflows/ci.yml", ".gitignore"]
rand_core = { version = "0.6", default-features = false }
dusk-bytes = "0.1"
dusk-bls12_381 = { version = "0.11", default-features = false }
dusk-bls12_381-sign = { version = "0.4", default-features = false }
dusk-jubjub = { version = "0.12", default-features = false }
dusk-poseidon = { version = "0.28", default-features = false }
dusk-pki = { version = "0.11", default-features = false }
Expand All @@ -31,6 +32,7 @@ rkyv-impl = [
"dusk-jubjub/rkyv-impl",
"dusk-pki/rkyv-impl",
"dusk-bls12_381/rkyv-impl",
"dusk-bls12_381-sign/rkyv-impl",
"rkyv",
"bytecheck"
]
107 changes: 57 additions & 50 deletions src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

//! Verifier data for the transfer circuits
mod stake;
pub use stake::*;

mod transfer;
pub use transfer::*;

use alloc::string::String;
use alloc::vec::Vec;

Expand All @@ -14,12 +20,11 @@ use rkyv::{Archive, Deserialize, Serialize};

use dusk_bls12_381::BlsScalar;
use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable};
use dusk_poseidon::cipher::PoseidonCipher;

use crate::{Crossover, Fee, Message, Note};
use crate::{Crossover, Fee, Note};

const STCO_MESSAGE_SIZE: usize = 7 + 2 * PoseidonCipher::cipher_size();
const STCT_MESSAGE_SIZE: usize = 5 + PoseidonCipher::cipher_size();
/// Type alias for the ID of a module.
pub type ModuleId = [u8; 32];

/// A phoenix transaction.
#[derive(Debug, Clone, PartialEq, Eq)]
Expand All @@ -46,38 +51,59 @@ pub struct Transaction {
/// A call to a contract. The `Vec<u8>` must be an `rkyv`ed representation
/// of the data the contract expects, and the `String` the name of the
/// function to call.
pub call: Option<(BlsScalar, String, Vec<u8>)>,
pub call: Option<(ModuleId, String, Vec<u8>)>,
}

impl Transaction {
/// Return input bytes to a hash function for the transaction.
/// Return the input bytes to a hash function for the transaction from its
/// components.
#[must_use]
pub fn to_hash_input_bytes(&self) -> Vec<u8> {
pub fn hash_input_bytes_from_components(
nullifiers: &[BlsScalar],
outputs: &[Note],
anchor: &BlsScalar,
fee: &Fee,
crossover: &Option<Crossover>,
call: &Option<(ModuleId, String, Vec<u8>)>,
) -> Vec<u8> {
let mut bytes = Vec::new();

for nullifier in &self.nullifiers {
for nullifier in nullifiers {
bytes.extend(nullifier.to_bytes());
}
for note in &self.outputs {
for note in outputs {
bytes.extend(note.to_bytes());
}

bytes.extend(self.anchor.to_bytes());
bytes.extend(self.fee.to_bytes());
bytes.extend(anchor.to_bytes());
bytes.extend(fee.to_bytes());

if let Some(crossover) = &self.crossover {
if let Some(crossover) = crossover {
bytes.extend(crossover.to_bytes());
}

if let Some((module, fn_name, call_data)) = &self.call {
bytes.extend(module.to_bytes());
if let Some((module, fn_name, call_data)) = call {
bytes.extend(module);
bytes.extend(fn_name.as_bytes());
bytes.extend(call_data);
}

bytes
}

/// Return input bytes to a hash function for the transaction.
#[must_use]
pub fn to_hash_input_bytes(&self) -> Vec<u8> {
Self::hash_input_bytes_from_components(
&self.nullifiers,
&self.outputs,
&self.anchor,
&self.fee,
&self.crossover,
&self.call,
)
}

/// Serialize the transaction to a variable length byte buffer.
#[allow(unused_must_use)]
pub fn to_var_bytes(&self) -> Vec<u8> {
Expand Down Expand Up @@ -113,7 +139,7 @@ impl Transaction {

if let Some((module, fn_name, call_data)) = &self.call {
bytes.push(1);
bytes.extend(module.to_bytes());
bytes.extend(module);

let size = fn_name.len() as u64;
bytes.extend(size.to_bytes());
Expand Down Expand Up @@ -159,8 +185,22 @@ impl Transaction {

let has_call = buffer[0] != 0;
let mut buffer = &buffer[1..];

let call = if has_call {
let module = BlsScalar::from_reader(&mut buffer)?;
let buffer_len = buffer.len();
if buffer.len() < 32 {
return Err(BytesError::BadLength {
found: buffer_len,
expected: 32,
});
}

let (module_buf, buf) = buffer.split_at(32);
buffer = buf;

let mut module = [0u8; 32];
module.copy_from_slice(module_buf);

let fn_name_size = u64::from_reader(&mut buffer)? as usize;
let fn_name = String::from_utf8(buffer[..fn_name_size].to_vec())
.map_err(|_err| BytesError::InvalidData)?;
Expand Down Expand Up @@ -202,40 +242,7 @@ impl Transaction {
}

/// Returns the call of the transaction.
pub fn call(&self) -> Option<&(BlsScalar, String, Vec<u8>)> {
pub fn call(&self) -> Option<&(ModuleId, String, Vec<u8>)> {
self.call.as_ref()
}
}

/// Signature message used for [`Stct`].
#[must_use]
pub fn process_message_stct(
crossover: &Crossover,
value: u64,
module_id: BlsScalar,
) -> [BlsScalar; STCT_MESSAGE_SIZE] {
let mut array = [BlsScalar::default(); STCT_MESSAGE_SIZE];
let hash_inputs = crossover.to_hash_inputs();
array[..hash_inputs.len()].copy_from_slice(&hash_inputs);
array[hash_inputs.len()..].copy_from_slice(&[value.into(), module_id]);
array
}

/// Signature message used for [`Stco`].
#[must_use]
pub fn process_message_stco(
crossover: &Crossover,
message: &Message,
module_id: BlsScalar,
) -> [BlsScalar; STCO_MESSAGE_SIZE] {
let mut array = [BlsScalar::default(); STCO_MESSAGE_SIZE];
let crossover_inputs = crossover.to_hash_inputs();
let message_inputs = message.to_hash_inputs();
array[..crossover_inputs.len()].copy_from_slice(&crossover_inputs);
array
[crossover_inputs.len()..crossover_inputs.len() + message_inputs.len()]
.copy_from_slice(&message_inputs);
array[crossover_inputs.len() + message_inputs.len()..]
.copy_from_slice(&[module_id]);
array
}
178 changes: 178 additions & 0 deletions src/transaction/stake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// 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 alloc::vec::Vec;

use dusk_bls12_381::BlsScalar;
use dusk_bls12_381_sign::{PublicKey, Signature};
use dusk_bytes::Serializable;
use dusk_pki::StealthAddress;
#[cfg(feature = "rkyv-impl")]
use rkyv::{Archive, Deserialize, Serialize};

use crate::note::Note;

const ALLOW_MESSAGE_SIZE: usize = u64::SIZE + PublicKey::SIZE;
const STAKE_MESSAGE_SIZE: usize = u64::SIZE + u64::SIZE;
const UNSTAKE_MESSAGE_SIZE: usize = u64::SIZE + Note::SIZE;
const WITHDRAW_MESSAGE_SIZE: usize =
u64::SIZE + StealthAddress::SIZE + BlsScalar::SIZE;

/// The representation of a public key's stake.
///
/// A user can stake for a particular `amount` larger in value than the
/// `MINIMUM_STAKE` value and is `reward`ed for participating in the consensus.
/// A stake is valid only after a particular block height - called the
/// eligibility.
///
/// To keep track of the number of interactions a public key has had with the
/// contract a `counter` is used to prevent replay attacks - where the same
/// signature could be used to prove ownership of the secret key in two
/// different transactions.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "rkyv-impl",
derive(Archive, Serialize, Deserialize),
archive_attr(derive(bytecheck::CheckBytes))
)]
pub struct StakeData {
/// Amount staked and eligibility.
pub amount: Option<(u64, u64)>,
/// The reward for participating in consensus.
pub reward: u64,
/// The signature counter to prevent replay.
pub counter: u64,
}

/// Stake a value on the stake contract.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "rkyv-impl",
derive(Archive, Serialize, Deserialize),
archive_attr(derive(bytecheck::CheckBytes))
)]
pub struct Stake {
/// Public key to which the stake will belong.
pub public_key: PublicKey,
/// Signature belonging to the given public key.
pub signature: Signature,
/// Value to stake.
pub value: u64,
/// Proof of the `STCT` circuit.
pub proof: Vec<u8>,
}

/// Unstake a value from the stake contract.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "rkyv-impl",
derive(Archive, Serialize, Deserialize),
archive_attr(derive(bytecheck::CheckBytes))
)]
pub struct Unstake {
/// Public key to unstake.
pub public_key: PublicKey,
/// Signature belonging to the given public key.
pub signature: Signature,
/// Note to withdraw to.
pub note: Note,
/// A proof of the `WFCT` circuit.
pub proof: Vec<u8>,
}

/// Withdraw the accumulated reward.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "rkyv-impl",
derive(Archive, Serialize, Deserialize),
archive_attr(derive(bytecheck::CheckBytes))
)]
pub struct Withdraw {
/// Public key to withdraw the rewards.
pub public_key: PublicKey,
/// Signature belonging to the given public key.
pub signature: Signature,
/// The address to mint to.
pub address: StealthAddress,
/// A nonce to prevent replay.
pub nonce: BlsScalar,
}

/// Allow a public key to stake.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "rkyv-impl",
derive(Archive, Serialize, Deserialize),
archive_attr(derive(bytecheck::CheckBytes))
)]
pub struct Allow {
/// The public key to allow staking to.
pub public_key: PublicKey,
/// The "owner" of the smart contract.
pub owner: PublicKey,
/// Signature of the `owner` key.
pub signature: Signature,
}

/// Signature message used for [`Allow`].
#[must_use]
pub fn allow_signature_message(
counter: u64,
staker: PublicKey,
) -> [u8; ALLOW_MESSAGE_SIZE] {
let mut bytes = [0u8; ALLOW_MESSAGE_SIZE];

bytes[..u64::SIZE].copy_from_slice(&counter.to_bytes());
bytes[u64::SIZE..].copy_from_slice(&staker.to_bytes());

bytes
}

/// Signature message used for [`Stake`].
#[must_use]
pub fn stake_signature_message(
counter: u64,
value: u64,
) -> [u8; STAKE_MESSAGE_SIZE] {
let mut bytes = [0u8; STAKE_MESSAGE_SIZE];

bytes[..u64::SIZE].copy_from_slice(&counter.to_bytes());
bytes[u64::SIZE..].copy_from_slice(&value.to_bytes());

bytes
}

/// Signature message used for [`Unstake`].
#[must_use]
pub fn unstake_signature_message(
counter: u64,
note: Note,
) -> [u8; UNSTAKE_MESSAGE_SIZE] {
let mut bytes = [0u8; UNSTAKE_MESSAGE_SIZE];

bytes[..u64::SIZE].copy_from_slice(&counter.to_bytes());
bytes[u64::SIZE..].copy_from_slice(&note.to_bytes());

bytes
}

/// Signature message used for [`Withdraw`].
#[must_use]
pub fn withdraw_signature_message(
counter: u64,
address: StealthAddress,
nonce: BlsScalar,
) -> [u8; WITHDRAW_MESSAGE_SIZE] {
let mut bytes = [0u8; WITHDRAW_MESSAGE_SIZE];

bytes[..u64::SIZE].copy_from_slice(&counter.to_bytes());
bytes[u64::SIZE..u64::SIZE + StealthAddress::SIZE]
.copy_from_slice(&address.to_bytes());
bytes[u64::SIZE + StealthAddress::SIZE..]
.copy_from_slice(&nonce.to_bytes());

bytes
}
Loading

0 comments on commit ce27314

Please sign in to comment.