Skip to content

Commit

Permalink
Merge pull request #294 from 0xPolygonMiden/dominik_test_falcon_authe…
Browse files Browse the repository at this point in the history
…ntication

feat: adding Falcon auth
  • Loading branch information
Dominik1999 authored Nov 9, 2023
2 parents 81c12c3 + 3704134 commit c278ce5
Show file tree
Hide file tree
Showing 5 changed files with 298 additions and 185 deletions.
66 changes: 42 additions & 24 deletions miden-lib/asm/eoa/basic.masm
Original file line number Diff line number Diff line change
@@ -1,32 +1,50 @@
use.miden::sat::account
use.miden::sat::tx
use.std::crypto::dsa::rpo_falcon512

#! Authenticate a transaction using Falcon
#! WIP - FALCON VERIFICATION IS MISSING, NOT SURE IF HASHING IN CORRECT ORDER
# CONSTANTS
# =================================================================================================

# Slot in account storage at which the public key is stored.
const.PUBLIC_KEY_SLOT=0

#! Authenticate a transaction using the Falcon signature scheme
#! Stack: []
#! Output: []
#!
#! - ASSET is the non-fungible asset of interest.
#! - tag is the tag to be included in the note.
#! - RECIPIENT is the recipient of the note.
export.auth_tx_rpo_falcon512
# update the nonce
push.1
exec.account::incr_nonce

# get commitments to consumed and created notes, new nonce, and ID
#exec.tx::get_output_notes_hash
#exec.tx::get_input_notes_hash
#exec.account::get_nonce push.0.0.0
#exec.account::get_id push.0.0.0

# compute the message to be signed
# M = h(output_notes_hash, h(input_notes_hash, h(0, 0, 0, id, 0, 0, 0, nonce)))
#hmerge hmerge hmerge

# get public key from account storage (assuming it is stored at index 0) and verify signature
#push.0
#exec.account::get_item
# [PUB_KEY, M]
# TODO: sign the message using FALCON
# Get commitments to created notes
exec.tx::get_output_notes_hash
# => [OUTPUT_NOTES_HASH, ...]

# Get commitments to consumed notes
exec.tx::get_input_notes_hash
# => [INPUT_NOTES_HASH, OUTPUT_NOTES_HASH, ...]

# Get current nonce of the account and pad
exec.account::get_nonce push.0.0.0
# => [0, 0, 0, nonce, INPUT_NOTES_HASH, OUTPUT_NOTES_HASH, ...]

# Get current AccountID and pad
exec.account::get_id push.0.0.0
# => [0, 0, 0, account_id, 0, 0, 0, nonce, INPUT_NOTES_HASH, OUTPUT_NOTES_HASH, ...]

# Compute the message to be signed
# M = h(OUTPUT_NOTES_HASH, h(INPUT_NOTES_HASH, h(0, 0, 0, account_id, 0, 0, 0, nonce)))
hmerge hmerge hmerge

# Get public key from account storage at pos 0 and verify signature
push.PUBLIC_KEY_SLOT exec.account::get_item
# => [PUB_KEY, M]

# Verify the signature against the public key and the message. The procedure gets as
# inputs the hash of the public key and the hash of the message via the operand
# stack. The signature is provided via the advice stack.The signature is valid if and
# only if the procedure returns.
exec.rpo_falcon512::verify
# => []

# Update the nonce
push.1 exec.account::incr_nonce
# => []
end
67 changes: 64 additions & 3 deletions miden-tx/tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use miden_lib::assembler::assembler;
use miden_objects::{
accounts::{Account, AccountId},
accounts::{Account, AccountCode, AccountId, AccountStorage, AccountVault},
assembly::ModuleAst,
notes::{Note, NoteOrigin, RecordedNote},
BlockHeader, ChainMmr, StarkField,
assembly::ProgramAst,
assets::{Asset, FungibleAsset},
crypto::{dsa::rpo_falcon512::KeyPair, merkle::MerkleStore, utils::Serializable},
notes::{Note, NoteOrigin, NoteScript, RecordedNote},
BlockHeader, ChainMmr, Felt, StarkField, Word,
};
use miden_tx::{DataStore, DataStoreError};
use mock::{
constants::{ACCOUNT_ID_SENDER, DEFAULT_ACCOUNT_CODE},
mock::account::MockAccountType,
mock::notes::AssetPreservationStatus,
mock::transaction::{mock_inputs, mock_inputs_with_existing},
Expand Down Expand Up @@ -78,3 +83,59 @@ impl DataStore for MockDataStore {
Ok(self.account.code().module().clone())
}
}

// HELPER FUNCTIONS
// ================================================================================================
pub fn get_new_key_pair_with_advice_map() -> (Word, Vec<Felt>) {
let keypair: KeyPair = KeyPair::new().unwrap();

let pk: Word = keypair.public_key().into();
let pk_sk_bytes = keypair.to_bytes();
let pk_sk_felts: Vec<Felt> =
pk_sk_bytes.iter().map(|a| Felt::new(*a as u64)).collect::<Vec<Felt>>();

(pk, pk_sk_felts)
}

#[allow(dead_code)]
pub fn get_account_with_default_account_code(
account_id: AccountId,
public_key: Word,
assets: Option<Asset>,
) -> Account {
let account_code_src = DEFAULT_ACCOUNT_CODE;
let account_code_ast = ModuleAst::parse(account_code_src).unwrap();
let mut account_assembler = assembler();

let account_code = AccountCode::new(account_code_ast.clone(), &mut account_assembler).unwrap();
let account_storage = AccountStorage::new(vec![(0, public_key)], MerkleStore::new()).unwrap();

let account_vault = match assets {
Some(asset) => AccountVault::new(&vec![asset.into()]).unwrap(),
None => AccountVault::new(&vec![]).unwrap(),
};

Account::new(account_id, account_vault, account_storage, account_code, Felt::new(1))
}

#[allow(dead_code)]
pub fn get_note_with_fungible_asset_and_script(
fungible_asset: FungibleAsset,
note_script: ProgramAst,
) -> Note {
let mut note_assembler = assembler();

let (note_script, _) = NoteScript::new(note_script, &mut note_assembler).unwrap();
const SERIAL_NUM: Word = [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)];
let sender_id = AccountId::try_from(ACCOUNT_ID_SENDER).unwrap();

Note::new(
note_script.clone(),
&[],
&vec![fungible_asset.into()],
SERIAL_NUM,
sender_id,
Felt::new(1),
)
.unwrap()
}
73 changes: 35 additions & 38 deletions miden-tx/tests/test_miden_faucet_contract.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
use miden_lib::{assembler::assembler, faucets::create_basic_faucet, AuthScheme};
use miden_objects::{
accounts::{Account, AccountCode, AccountId, AccountVault},
accounts::{Account, AccountCode, AccountId, AccountStorage, AccountVault},
assembly::{ModuleAst, ProgramAst},
assets::{Asset, FungibleAsset, TokenSymbol},
crypto::dsa::rpo_falcon512,
notes::{Note, NoteMetadata, NoteScript, NoteStub, NoteVault},
crypto::{
dsa::rpo_falcon512::{KeyPair, PublicKey},
merkle::MerkleStore,
},
notes::{NoteMetadata, NoteStub, NoteVault},
Felt, StarkField, Word, ZERO,
};
use mock::{
constants::{ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN, ACCOUNT_ID_SENDER},
mock::account::mock_account_storage,
utils::prepare_word,
};
use mock::{constants::ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN, utils::prepare_word};

use miden_tx::TransactionExecutor;

mod common;
use common::MockDataStore;

use vm_core::crypto::dsa::rpo_falcon512::{KeyPair, PublicKey};
use common::{
get_new_key_pair_with_advice_map, get_note_with_fungible_asset_and_script, MockDataStore,
};

#[test]
fn test_faucet_contract_mint_fungible_asset_succeeds() {
let faucet_account = get_faucet_account_with_max_supply_and_total_issuance(200, None);
let (faucet_pub_key, faucet_keypair_felts) = get_new_key_pair_with_advice_map();
let faucet_account =
get_faucet_account_with_max_supply_and_total_issuance(faucet_pub_key, 200, None);

// CONSTRUCT AND EXECUTE TX (Success)
// --------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -64,7 +65,9 @@ fn test_faucet_contract_mint_fungible_asset_succeeds() {
.as_str(),
)
.unwrap();
let tx_script = executor.compile_tx_script(tx_script_code, vec![], vec![]).unwrap();
let tx_script = executor
.compile_tx_script(tx_script_code, vec![(faucet_pub_key, faucet_keypair_felts)], vec![])
.unwrap();

// Execute the transaction and get the witness
let transaction_result = executor
Expand All @@ -89,7 +92,9 @@ fn test_faucet_contract_mint_fungible_asset_succeeds() {

#[test]
fn test_faucet_contract_mint_fungible_asset_fails_exceeds_max_supply() {
let faucet_account = get_faucet_account_with_max_supply_and_total_issuance(200, None);
let (faucet_pub_key, faucet_keypair_felts) = get_new_key_pair_with_advice_map();
let faucet_account =
get_faucet_account_with_max_supply_and_total_issuance(faucet_pub_key.clone(), 200, None);

// CONSTRUCT AND EXECUTE TX (Failure)
// --------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -131,7 +136,9 @@ fn test_faucet_contract_mint_fungible_asset_fails_exceeds_max_supply() {
.as_str(),
)
.unwrap();
let tx_script = executor.compile_tx_script(tx_script_code, vec![], vec![]).unwrap();
let tx_script = executor
.compile_tx_script(tx_script_code, vec![(faucet_pub_key, faucet_keypair_felts)], vec![])
.unwrap();

// Execute the transaction and get the witness
let transaction_result = executor.execute_transaction(
Expand All @@ -146,7 +153,13 @@ fn test_faucet_contract_mint_fungible_asset_fails_exceeds_max_supply() {

#[test]
fn test_faucet_contract_burn_fungible_asset_succeeds() {
let faucet_account = get_faucet_account_with_max_supply_and_total_issuance(200, Some(100));
let (faucet_pub_key, _faucet_keypair_felts) = get_new_key_pair_with_advice_map();
let faucet_account = get_faucet_account_with_max_supply_and_total_issuance(
faucet_pub_key.clone(),
200,
Some(100),
);

let fungible_asset = FungibleAsset::new(faucet_account.id(), 100).unwrap();

// check that max_supply (slot 1) is 200 and amount already issued (slot 255) is 100
Expand Down Expand Up @@ -179,7 +192,7 @@ fn test_faucet_contract_burn_fungible_asset_succeeds() {
)
.unwrap();

let note = get_note_with_asset_and_script(fungible_asset.clone(), note_script);
let note = get_note_with_fungible_asset_and_script(fungible_asset.clone(), note_script);

// CONSTRUCT AND EXECUTE TX (Success)
// --------------------------------------------------------------------------------------------
Expand All @@ -206,7 +219,7 @@ fn test_faucet_contract_burn_fungible_asset_succeeds() {
#[test]
fn test_faucet_contract_creation() {
// we need a Falcon Public Key to create the wallet account
let key_pair: KeyPair = rpo_falcon512::KeyPair::new().unwrap();
let key_pair: KeyPair = KeyPair::new().unwrap();
let pub_key: PublicKey = key_pair.public_key();
let auth_scheme: AuthScheme = AuthScheme::RpoFalcon512 {
pub_key: pub_key.into(),
Expand Down Expand Up @@ -246,6 +259,7 @@ fn test_faucet_contract_creation() {
}

fn get_faucet_account_with_max_supply_and_total_issuance(
public_key: Word,
max_supply: u64,
total_issuance: Option<u64>,
) -> Account {
Expand All @@ -257,9 +271,10 @@ fn get_faucet_account_with_max_supply_and_total_issuance(
let faucet_account_code =
AccountCode::new(faucet_account_code_ast.clone(), &mut account_assembler).unwrap();

let mut faucet_account_storage = mock_account_storage();
let faucet_storage_slot_1 = [Felt::new(max_supply), Felt::new(0), Felt::new(0), Felt::new(0)];
faucet_account_storage.set_item(1, faucet_storage_slot_1);
let mut faucet_account_storage =
AccountStorage::new(vec![(0, public_key), (1, faucet_storage_slot_1)], MerkleStore::new())
.unwrap();

if total_issuance.is_some() {
let faucet_storage_slot_255 =
Expand All @@ -275,21 +290,3 @@ fn get_faucet_account_with_max_supply_and_total_issuance(
Felt::new(1),
)
}

fn get_note_with_asset_and_script(fungible_asset: FungibleAsset, note_script: ProgramAst) -> Note {
let mut note_assembler = assembler();

let (note_script, _) = NoteScript::new(note_script, &mut note_assembler).unwrap();
const SERIAL_NUM: Word = [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)];
let sender_id = AccountId::try_from(ACCOUNT_ID_SENDER).unwrap();

Note::new(
note_script.clone(),
&[],
&vec![fungible_asset.into()],
SERIAL_NUM,
sender_id,
Felt::new(1),
)
.unwrap()
}
Loading

0 comments on commit c278ce5

Please sign in to comment.