Skip to content

Commit

Permalink
rusk: support Moonlight transactions
Browse files Browse the repository at this point in the history
To support Moonlight transactions, we leverage the changes made to
`execution-core` to unify the handling of both transaction models using
the exact same code path as before.
  • Loading branch information
Eduardo Leegwater Simões committed Jul 15, 2024
1 parent cde2cf5 commit 359f47c
Show file tree
Hide file tree
Showing 26 changed files with 447 additions and 276 deletions.
28 changes: 19 additions & 9 deletions rusk/benches/block_ingestion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use criterion::{
criterion_group, criterion_main, BenchmarkGroup, BenchmarkId, Criterion,
};
use execution_core::{
transfer::Transaction as PhoenixTransaction, StakePublicKey, StakeSecretKey,
transfer::Transaction as ProtocolTransaction, AccountPublicKey,
AccountSecretKey,
};
use node_data::ledger::Transaction;
use rand::prelude::StdRng;
Expand All @@ -37,7 +38,7 @@ fn load_txs() -> Vec<Transaction> {
for line in BufReader::new(TXS_BYTES).lines() {
let line = line.unwrap();
let tx_bytes = hex::decode(line).unwrap();
let tx = PhoenixTransaction::from_slice(&tx_bytes).unwrap();
let tx = ProtocolTransaction::from_slice(&tx_bytes).unwrap();
txs.push(Transaction {
version: 1,
r#type: 0,
Expand All @@ -46,12 +47,21 @@ fn load_txs() -> Vec<Transaction> {
}

for tx in txs.iter() {
match rusk::verifier::verify_proof(&tx.inner) {
Ok(true) => Ok(()),
Ok(false) => Err(anyhow::anyhow!("Invalid proof")),
Err(e) => Err(anyhow::anyhow!("Cannot verify the proof: {e}")),
match &tx.inner {
ProtocolTransaction::Phoenix(tx) => {
match rusk::verifier::verify_proof(tx) {
Ok(true) => Ok(()),
Ok(false) => Err(anyhow::anyhow!("Invalid proof")),
Err(e) => {
Err(anyhow::anyhow!("Cannot verify the proof: {e}"))
}
}
.unwrap()
}
ProtocolTransaction::Moonlight(_) => {
panic!("All transactions must be phoenix")
}
}
.unwrap()
}

txs
Expand Down Expand Up @@ -80,8 +90,8 @@ pub fn accept_benchmark(c: &mut Criterion) {

let generator = {
let mut rng = StdRng::seed_from_u64(0xbeef);
let sk = StakeSecretKey::random(&mut rng);
StakePublicKey::from(&sk)
let sk = AccountSecretKey::random(&mut rng);
AccountPublicKey::from(&sk)
};

const BLOCK_HEIGHT: u64 = 1;
Expand Down
58 changes: 32 additions & 26 deletions rusk/src/lib/chain/rusk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ use dusk_consensus::operations::{
CallParams, VerificationOutput, VoterWithCredits,
};
use execution_core::{
stake::StakeData, transfer::Transaction as PhoenixTransaction, BlsScalar,
StakePublicKey,
stake::StakeData,
transfer::{AccountData, Transaction as ProtocolTransaction},
AccountPublicKey, BlsScalar,
};
use node_data::ledger::{SpentTransaction, Transaction};
use rusk_abi::dusk::Dusk;
Expand All @@ -41,9 +42,9 @@ use crate::http::RuesEvent;
use crate::Error::InvalidCreditsCount;
use crate::{Error, Result};

pub static DUSK_KEY: LazyLock<StakePublicKey> = LazyLock::new(|| {
pub static DUSK_KEY: LazyLock<AccountPublicKey> = LazyLock::new(|| {
let dusk_cpk_bytes = include_bytes!("../../assets/dusk.cpk");
StakePublicKey::from_slice(dusk_cpk_bytes)
AccountPublicKey::from_slice(dusk_cpk_bytes)
.expect("Dusk consensus public key to be valid")
});

Expand Down Expand Up @@ -121,7 +122,7 @@ impl Rusk {
}
}
let tx_id = hex::encode(unspent_tx.id());
if unspent_tx.inner.payload().fee.gas_limit > block_gas_left {
if unspent_tx.inner.gas_limit() > block_gas_left {
info!("Skipping {tx_id} due gas_limit greater than left: {block_gas_left}");
continue;
}
Expand Down Expand Up @@ -154,7 +155,7 @@ impl Rusk {
update_hasher(&mut event_hasher, &receipt.events);

block_gas_left -= gas_spent;
let gas_price = unspent_tx.inner.payload().fee.gas_price;
let gas_price = unspent_tx.inner.gas_price();
dusk_spent += gas_spent * gas_price;
spent_txs.push(SpentTransaction {
inner: unspent_tx,
Expand Down Expand Up @@ -200,9 +201,9 @@ impl Rusk {
&self,
block_height: u64,
block_gas_limit: u64,
generator: &StakePublicKey,
generator: &AccountPublicKey,
txs: &[Transaction],
missed_generators: &[StakePublicKey],
missed_generators: &[AccountPublicKey],
voters: Option<&[VoterWithCredits]>,
) -> Result<(Vec<SpentTransaction>, VerificationOutput)> {
let session = self.session(block_height, None)?;
Expand All @@ -229,10 +230,10 @@ impl Rusk {
&self,
block_height: u64,
block_gas_limit: u64,
generator: StakePublicKey,
generator: AccountPublicKey,
txs: Vec<Transaction>,
consistency_check: Option<VerificationOutput>,
missed_generators: &[StakePublicKey],
missed_generators: &[AccountPublicKey],
voters: Option<&[VoterWithCredits]>,
) -> Result<(Vec<SpentTransaction>, VerificationOutput)> {
let session = self.session(block_height, None)?;
Expand Down Expand Up @@ -315,16 +316,21 @@ impl Rusk {
pub fn provisioners(
&self,
base_commit: Option<[u8; 32]>,
) -> Result<impl Iterator<Item = (StakePublicKey, StakeData)>> {
) -> Result<impl Iterator<Item = (AccountPublicKey, StakeData)>> {
let (sender, receiver) = mpsc::channel();
self.feeder_query(STAKE_CONTRACT, "stakes", &(), sender, base_commit)?;
Ok(receiver.into_iter().map(|bytes| {
rkyv::from_bytes::<(StakePublicKey, StakeData)>(&bytes).expect(
rkyv::from_bytes::<(AccountPublicKey, StakeData)>(&bytes).expect(
"The contract should only return (pk, stake_data) tuples",
)
}))
}

/// Returns an account's information.
pub fn account(&self, pk: &AccountPublicKey) -> Result<AccountData> {
self.query(TRANSFER_CONTRACT, "account", pk)
}

/// Fetches the previous state data for stake changes in the contract.
///
/// Communicates with the stake contract to obtain information about the
Expand All @@ -339,12 +345,12 @@ impl Rusk {
/// # Returns
///
/// Returns a Result containing an iterator over tuples. Each tuple consists
/// of a `StakePublicKey` and an optional `StakeData`, representing the
/// of a `AccountPublicKey` and an optional `StakeData`, representing the
/// state data before the last changes in the stake contract.
pub fn last_provisioners_change(
&self,
base_commit: Option<[u8; 32]>,
) -> Result<Vec<(StakePublicKey, Option<StakeData>)>> {
) -> Result<Vec<(AccountPublicKey, Option<StakeData>)>> {
let (sender, receiver) = mpsc::channel();
self.feeder_query(
STAKE_CONTRACT,
Expand All @@ -354,15 +360,15 @@ impl Rusk {
base_commit,
)?;
Ok(receiver.into_iter().map(|bytes| {
rkyv::from_bytes::<(StakePublicKey, Option<StakeData>)>(&bytes).expect(
rkyv::from_bytes::<(AccountPublicKey, Option<StakeData>)>(&bytes).expect(
"The contract should only return (pk, Option<stake_data>) tuples",
)
}).collect())
}

pub fn provisioner(
&self,
pk: &StakePublicKey,
pk: &AccountPublicKey,
) -> Result<Option<StakeData>> {
self.query(STAKE_CONTRACT, "get_stake", pk)
}
Expand Down Expand Up @@ -416,9 +422,9 @@ fn accept(
session: Session,
block_height: u64,
block_gas_limit: u64,
generator: &StakePublicKey,
generator: &AccountPublicKey,
txs: &[Transaction],
missed_generators: &[StakePublicKey],
missed_generators: &[AccountPublicKey],
voters: Option<&[VoterWithCredits]>,
) -> Result<(
Vec<SpentTransaction>,
Expand All @@ -445,7 +451,7 @@ fn accept(

let gas_spent = receipt.gas_spent;

dusk_spent += gas_spent * tx.payload().fee.gas_price;
dusk_spent += gas_spent * tx.gas_price();
block_gas_left = block_gas_left
.checked_sub(gas_spent)
.ok_or(Error::OutOfGas)?;
Expand Down Expand Up @@ -498,15 +504,15 @@ fn accept(
/// optional contract call in step 1.
fn execute(
session: &mut Session,
tx: &PhoenixTransaction,
tx: &ProtocolTransaction,
) -> Result<CallReceipt<Result<Vec<u8>, ContractError>>, PiecrustError> {
// Spend the inputs and execute the call. If this errors the transaction is
// unspendable.
let mut receipt = session.call::<_, Result<Vec<u8>, ContractError>>(
TRANSFER_CONTRACT,
"spend_and_execute",
tx,
tx.payload().fee.gas_limit,
tx.gas_limit(),
)?;

// Ensure all gas is consumed if there's an error in the contract call
Expand All @@ -521,7 +527,7 @@ fn execute(
.call::<_, ()>(
TRANSFER_CONTRACT,
"refund",
&(tx.payload().fee, receipt.gas_spent),
&receipt.gas_spent,
u64::MAX,
)
.expect("Refunding must succeed");
Expand All @@ -543,9 +549,9 @@ fn reward_slash_and_update_root(
session: &mut Session,
block_height: u64,
dusk_spent: Dusk,
generator: &StakePublicKey,
slashing: &[StakePublicKey],
voters: Option<&[(StakePublicKey, usize)]>,
generator: &AccountPublicKey,
slashing: &[AccountPublicKey],
voters: Option<&[(AccountPublicKey, usize)]>,
) -> Result<Vec<Event>> {
let (
dusk_value,
Expand Down Expand Up @@ -659,7 +665,7 @@ fn calc_generator_extra_reward(
credits.saturating_sub(sum as u64) * reward_per_quota
}

fn to_bs58(pk: &StakePublicKey) -> String {
fn to_bs58(pk: &AccountPublicKey) -> String {
let mut pk = bs58::encode(&pk.to_bytes()).into_string();
pk.truncate(16);
pk
Expand Down
92 changes: 71 additions & 21 deletions rusk/src/lib/chain/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ use dusk_consensus::operations::{
};
use dusk_consensus::user::provisioners::Provisioners;
use dusk_consensus::user::stake::Stake;
use execution_core::{stake::StakeData, StakePublicKey};
use execution_core::{
stake::StakeData, transfer::Transaction as ProtocolTransaction,
AccountPublicKey,
};
use node::vm::VMExecution;
use node_data::ledger::{Block, SpentTransaction, Transaction};

Expand Down Expand Up @@ -47,7 +50,7 @@ impl VMExecution for Rusk {
) -> anyhow::Result<VerificationOutput> {
info!("Received verify_state_transition request");
let generator = blk.header().generator_bls_pubkey;
let generator = StakePublicKey::from_slice(&generator.0)
let generator = AccountPublicKey::from_slice(&generator.0)
.map_err(|e| anyhow::anyhow!("Error in from_slice {e:?}"))?;

let (_, verification_output) = self
Expand All @@ -71,7 +74,7 @@ impl VMExecution for Rusk {
) -> anyhow::Result<(Vec<SpentTransaction>, VerificationOutput)> {
info!("Received accept request");
let generator = blk.header().generator_bls_pubkey;
let generator = StakePublicKey::from_slice(&generator.0)
let generator = AccountPublicKey::from_slice(&generator.0)
.map_err(|e| anyhow::anyhow!("Error in from_slice {e:?}"))?;

let (txs, verification_output) = self
Expand Down Expand Up @@ -105,18 +108,64 @@ impl VMExecution for Rusk {
fn preverify(&self, tx: &Transaction) -> anyhow::Result<()> {
info!("Received preverify request");
let tx = &tx.inner;
let existing_nullifiers = self
.existing_nullifiers(&tx.payload().tx_skeleton.nullifiers)
.map_err(|e| anyhow::anyhow!("Cannot check nullifiers: {e}"))?;

if !existing_nullifiers.is_empty() {
let err = crate::Error::RepeatingNullifiers(existing_nullifiers);
return Err(anyhow::anyhow!("Invalid tx: {err}"));
}
match crate::verifier::verify_proof(tx) {
Ok(true) => Ok(()),
Ok(false) => Err(anyhow::anyhow!("Invalid proof")),
Err(e) => Err(anyhow::anyhow!("Cannot verify the proof: {e}")),
match tx {
ProtocolTransaction::Phoenix(tx) => {
let payload = tx.payload();

let existing_nullifiers = self
.existing_nullifiers(&payload.tx_skeleton.nullifiers)
.map_err(|e| {
anyhow::anyhow!("Cannot check nullifiers: {e}")
})?;

if !existing_nullifiers.is_empty() {
let err =
crate::Error::RepeatingNullifiers(existing_nullifiers);
return Err(anyhow::anyhow!("Invalid tx: {err}"));
}

match crate::verifier::verify_proof(tx) {
Ok(true) => Ok(()),
Ok(false) => Err(anyhow::anyhow!("Invalid proof")),
Err(e) => {
Err(anyhow::anyhow!("Cannot verify the proof: {e}"))
}
}
}
ProtocolTransaction::Moonlight(tx) => {
let payload = tx.payload();

let account_data =
self.account(&payload.from).map_err(|e| {
anyhow::anyhow!("Cannot check account: {e}")
})?;

let max_value = payload.value
+ payload.deposit
+ payload.gas_limit * payload.gas_price;
if max_value > account_data.balance {
return Err(anyhow::anyhow!(
"Value spent larger than account holds"
));
}

if payload.nonce <= account_data.nonce {
let err = crate::Error::RepeatingNonce(
payload.from.into(),
payload.nonce,
);
return Err(anyhow::anyhow!("Invalid tx: {err}"));
}

match crate::verifier::verify_signature(tx) {
Ok(true) => Ok(()),
Ok(false) => Err(anyhow::anyhow!("Invalid signature")),
Err(e) => {
Err(anyhow::anyhow!("Cannot verify the signature: {e}"))
}
}
}
}
}

Expand All @@ -136,15 +185,12 @@ impl VMExecution for Rusk {

fn get_provisioner(
&self,
pk: &StakePublicKey,
pk: &AccountPublicKey,
) -> anyhow::Result<Option<Stake>> {
let stake = self
.provisioner(pk)
.map_err(|e| anyhow::anyhow!("Cannot get provisioner {e}"))?
.map(|stake| {
let (value, eligibility) = stake.amount.unwrap_or_default();
Stake::new(value, stake.reward, eligibility, stake.counter)
});
.map(Self::to_stake);
Ok(stake)
}

Expand Down Expand Up @@ -214,7 +260,11 @@ impl Rusk {
}

fn to_stake(stake: StakeData) -> Stake {
let (value, eligibility) = stake.amount.unwrap_or_default();
Stake::new(value, stake.reward, eligibility, stake.counter)
let stake_amount = stake.amount.unwrap_or_default();

let value = stake_amount.value;
let eligibility = stake_amount.eligibility;

Stake::new(value, stake.reward, eligibility, stake.nonce)
}
}
Loading

0 comments on commit 359f47c

Please sign in to comment.