Skip to content

Commit

Permalink
transfer: add bridge function
Browse files Browse the repository at this point in the history
This commit introduces a new bridge functionality to the transfer
contract's state. This function enables any sequencer to register as a
bridge provider upon staking a balance into the bridge. The authorized
sequencer can then facilitate up to the amount of their stake in bridge
operations.

Adopting an optimistic approach, the bridge will process transactions
without immediately verifying L1 associated events. However, it will
keep track of these events and wait for several blocks before
considering a transaction finalized. If a fraud proof is presented,
demonstrating that a bridge operation was carried out without a valid L1
event, the responsible sequencer stands to lose all rewards and staked
amount. In contrast, the individual who exposed the fraudulent activity
will be rewarded with the ill-gotten amount.
  • Loading branch information
artifex11 committed May 19, 2024
1 parent ec0621c commit 693568d
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 1 deletion.
1 change: 1 addition & 0 deletions contracts/transfer-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2021"

[dependencies]
dusk-bls12_381 = { version = "0.13", default-features = false, features = ["rkyv-impl"] }
bls12_381-bls = { version = "0.2", default-features = false, features = ["rkyv-impl"] }
dusk-jubjub = { version = "0.14", default-features = false, features = ["rkyv-impl"] }
dusk-poseidon = { version = "0.33", default-features = false, features = ["rkyv-impl", "alloc"] }
phoenix-core = { version = "0.26", default-features = false, features = ["rkyv-impl", "alloc"] }
Expand Down
23 changes: 23 additions & 0 deletions contracts/transfer-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ extern crate alloc;
use alloc::vec::Vec;

use dusk_bls12_381::BlsScalar;
use bls12_381_bls::PublicKey;

use bytecheck::CheckBytes;
use phoenix_core::{Note, StealthAddress};
Expand Down Expand Up @@ -90,3 +91,25 @@ pub struct Mint {
/// A nonce to prevent replay.
pub nonce: BlsScalar,
}

/// Locked staked amount for the bridge.
#[derive(Debug, Clone, Archive, Deserialize, Serialize)]
#[archive_attr(derive(CheckBytes))]
pub struct Bridge {
/// Address of the sequencer that performed the bridge operation.
pub sequencer: PublicKey,
/// Benefitiary of the bridge operation.
pub receiver: PublicKey,
/// Block number of the operation.
pub block: u64,
/// Fee paid to the sequencer.
pub fee: u64,
/// Value to be minted for the receiver.
pub value: u64,
/// Block number that contains the event.
pub event_block: u64,
/// Bridge event on L1.
pub event: Vec<u8>,
/// Proof of validity of the sequencer authorship.
pub proof: Vec<u8>,
}
1 change: 1 addition & 0 deletions contracts/transfer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ crate-type = ["cdylib", "rlib"]

[dependencies]
dusk-bls12_381 = { version = "0.13", default-features = false, features = ["rkyv-impl"] }
bls12_381-bls = { version = "0.2", default-features = false, features = ["rkyv-impl"] }
dusk-bytes = "0.1"
dusk-jubjub = { version = "0.14", default-features = false, features = ["rkyv-impl"] }
jubjub-schnorr = { version = "0.2", default-features = false, features = ["rkyv-impl"] }
Expand Down
237 changes: 236 additions & 1 deletion contracts/transfer/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use alloc::collections::btree_map::Entry;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::vec::Vec;

use bls12_381_bls::PublicKey;
use dusk_bls12_381::BlsScalar;
use dusk_bytes::DeserializableSlice;
use dusk_jubjub::JubJubAffine;
Expand All @@ -22,7 +23,7 @@ use ringbuffer::{ConstGenericRingBuffer, RingBuffer};
use rusk_abi::{
ContractError, ContractId, PaymentInfo, PublicInput, STAKE_CONTRACT,
};
use transfer_contract_types::{Mint, Stct, Wfct, Wfctc};
use transfer_contract_types::{Bridge, Mint, Stct, Wfct, Wfctc};

/// Arity of the transfer tree.
pub const A: usize = 4;
Expand All @@ -35,6 +36,11 @@ pub struct TransferState {
nullifiers: BTreeSet<BlsScalar>,
roots: ConstGenericRingBuffer<BlsScalar, MAX_ROOTS>,
balances: BTreeMap<ContractId, u64>,
bridge_staked: BTreeMap<[u8; PublicKey::SIZE], u64>,
bridge: BTreeMap<[u8; PublicKey::SIZE], Vec<Bridge>>,
bridge_rewards: BTreeMap<[u8; PublicKey::SIZE], u64>,
bridge_blocks_delay: u64,
bridge_standard_locked_amount: u64,
var_crossover: Option<Crossover>,
var_crossover_addr: Option<StealthAddress>,
}
Expand Down Expand Up @@ -169,6 +175,235 @@ impl TransferState {
true
}

/// Adds the specified value to the staked amount for bridging, utilizing
/// the given sequencer public key. The proof will mirror that of a
/// "send to contract transparent", asserting the consumption of a
/// Phoenix Note value to augment the staked balance.
pub fn bridge_stake(
&mut self,
sequencer: PublicKey,
value: u64,
proof: Vec<u8>,
) {
let mut pi = Vec::with_capacity(3);
let balance;
match self.bridge_staked.entry(sequencer) {
Entry::Vacant(ve) => {
balance = value;
ve.insert(value);
}
Entry::Occupied(mut oe) => {
let v = oe.get_mut();
balance = value + *v;
*v += value;
}
}

let vd = todo!();
Self::assert_proof(vd, proof, pi)
.expect("Failed to verify the provided proof!");
}

pub fn bridge(
&mut self,
sequencer: PublicKey,
receiver: PublicKey,
fee: u64,
value: u64,
event: Vec<u8>,
proof: Vec<u8>,
) {
let mut pi = Vec::with_capacity(3);

if value == 0 {
panic!("The bridged value is invalid.");
}

if value < fee {
panic!("The bridged value is not enough to pay the sequencer fee.");
}

let balance = self
.bridge_staked
.get(sequencer.to_bytes())
.copied()
.unwrap_or_default();
let mut rewards = fee
+ self
.bridge_rewards
.get(sequencer.to_bytes())
.copied()
.unwrap_or_default();
let bridged = self
.bridge
.get(sequencer.to_bytes())
.copied()
.unwrap_or_default();
let bridged_adjusted = Vec::with_capacity(bridged.len() + 1);
let mut locked = self.bridge_standard_locked_amount;

// Locked amount will expire after `self.bridge_blocks_delay` blocks.
// Such interval should be enough to assume a fraud proof is
// inexistent.
for bridge in bridged {
if bridge.block + self.bridge_blocks_delay
< rusk_abi::block_height()
{
// the bridge operation is still locked under the delay.
locked += bridge.value;
bridged_adjusted.push(bridge);
} else {
// the bridge operation outlived the delay; remove from vector &
// add fee reward
rewards += bridge.fee;
}
}

let balance = balance.saturating_sub(locked);
if balance < value {
panic!("The sequencer does not have enough balance to perform this operation.");
}

self.bridge.insert(sequencer.to_bytes(), bridged_adjusted);
self.bridge_rewards.insert(sequencer.to_bytes(), rewards);
self.push_note_current_height(todo!(
"receiver note with `value - fee`"
));

let vd = todo!();
Self::assert_proof(vd, proof, pi)
.expect("Failed to verify the provided proof!");
}

pub fn bridge_withdraw_rewards(
&mut self,
sequencer: PublicKey,
proof: Vec<u8>,
) {
let mut pi = Vec::with_capacity(3);

let mut rewards = self
.bridge_rewards
.get(sequencer.to_bytes())
.copied()
.unwrap_or_default();

let bridged = self
.bridge
.get(sequencer.to_bytes())
.copied()
.unwrap_or_default();
let bridged_adjusted = Vec::with_capacity(bridged.len() + 1);

for bridge in bridged {
if bridge.block + self.bridge_blocks_delay
< rusk_abi::block_height()
{
bridged_adjusted.push(bridge);
} else {
rewards += bridge.fee;
}
}

self.bridge_rewards.insert(sequencer.to_bytes(), 0);
self.bridge.insert(sequencer.to_bytes(), bridged_adjusted);
self.push_note_current_height(todo!("sequencer rewards note"));

let vd = todo!();
Self::assert_proof(vd, proof, pi)
.expect("Failed to verify the provided proof!");
}

pub fn bridge_withdraw_stake(
&mut self,
sequencer: PublicKey,
proof: Vec<u8>,
) {
let mut pi = Vec::with_capacity(3);

let mut rewards = self
.bridge_rewards
.get(sequencer.to_bytes())
.copied()
.unwrap_or_default();

let bridged = self
.bridge
.get(sequencer.to_bytes())
.copied()
.unwrap_or_default();

let bridged_adjusted = Vec::with_capacity(bridged.len() + 1);

for bridge in bridged {
if bridge.block + self.bridge_blocks_delay
< rusk_abi::block_height()
{
bridged_adjusted.push(bridge);
} else {
rewards += bridge.fee;
}
}

if !bridged_adjusted.is_empty() {
panic!("the sequencer is not allowed to withdraw stake white bridged operations are locked.");
}

let balance = self
.bridge_staked
.get(sequencer.to_bytes())
.copied()
.unwrap_or_default();

self.bridge_rewards.insert(sequencer.to_bytes(), 0);
self.bridge_staked.insert(sequencer.to_bytes(), 0);
self.bridge.insert(sequencer.to_bytes(), bridged_adjusted);
self.push_note_current_height(todo!(
"sequencer balance + rewards note"
));

let vd = todo!();
Self::assert_proof(vd, proof, pi)
.expect("Failed to verify the provided proof!");
}

/// This function accepts a proof that one bridge operation was performed
/// without a corresponding valid L1 event.
///
/// The sequencer proven to have performed a fraud will lose all his rewards
/// and stake to the prover.
pub fn bridge_fraud(
&mut self,
sequencer: PublicKey,
prover: PublicKey,
operation: Bridge,
proof: Vec<u8>,
) {
let mut pi = Vec::with_capacity(3);

let rewards = self
.bridge_rewards
.get(sequencer.to_bytes())
.copied()
.unwrap_or_default();

let balance = self
.bridge_staked
.get(sequencer.to_bytes())
.copied()
.unwrap_or_default();

self.push_note_current_height(todo!("prover balance + rewards note"));

self.bridge_rewards.insert(sequencer.to_bytes(), 0);
self.bridge_staked.insert(sequencer.to_bytes(), 0);
self.bridge.insert(sequencer.to_bytes(), Vec::new());

let vd = todo!();
Self::assert_proof(vd, proof, pi)
.expect("Failed to verify the provided proof!");
}

/// Spends the inputs and creates the given UTXO, and executes the contract
/// call if present. It performs all checks necessary to ensure the
/// transaction is valid - hash matches, anchor has been a root of the
Expand Down

0 comments on commit 693568d

Please sign in to comment.