mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-15 12:39:30 +00:00
refactor: use faucet program to manage faucet account
This commit is contained in:
parent
e359c1abe2
commit
ee5a98fc48
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -2666,6 +2666,14 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "faucet_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nssa_core",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ferroid"
|
||||
version = "2.0.0"
|
||||
@ -3938,6 +3946,7 @@ dependencies = [
|
||||
"bytesize",
|
||||
"common",
|
||||
"env_logger",
|
||||
"faucet_core",
|
||||
"futures",
|
||||
"hex",
|
||||
"indexer_ffi",
|
||||
@ -6322,6 +6331,7 @@ dependencies = [
|
||||
"borsh",
|
||||
"clock_core",
|
||||
"env_logger",
|
||||
"faucet_core",
|
||||
"hex",
|
||||
"hex-literal 1.1.0",
|
||||
"k256",
|
||||
@ -7057,6 +7067,7 @@ dependencies = [
|
||||
"ata_program",
|
||||
"authenticated_transfer_core",
|
||||
"clock_core",
|
||||
"faucet_core",
|
||||
"nssa_core",
|
||||
"risc0-zkvm",
|
||||
"serde",
|
||||
@ -8421,6 +8432,7 @@ dependencies = [
|
||||
"bytesize",
|
||||
"chrono",
|
||||
"common",
|
||||
"faucet_core",
|
||||
"futures",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
|
||||
@ -21,6 +21,7 @@ members = [
|
||||
"programs/associated_token_account/core",
|
||||
"programs/associated_token_account",
|
||||
"programs/authenticated_transfer/core",
|
||||
"programs/faucet/core",
|
||||
"programs/vault/core",
|
||||
"sequencer/core",
|
||||
"sequencer/service",
|
||||
@ -68,6 +69,7 @@ amm_program = { path = "programs/amm" }
|
||||
ata_core = { path = "programs/associated_token_account/core" }
|
||||
ata_program = { path = "programs/associated_token_account" }
|
||||
authenticated_transfer_core = { path = "programs/authenticated_transfer/core" }
|
||||
faucet_core = { path = "programs/faucet/core" }
|
||||
vault_core = { path = "programs/vault/core" }
|
||||
test_program_methods = { path = "test_program_methods" }
|
||||
testnet_initial_state = { path = "testnet_initial_state" }
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
artifacts/program_methods/faucet.bin
Normal file
BIN
artifacts/program_methods/faucet.bin
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -68,7 +68,8 @@ impl NSSATransaction {
|
||||
|
||||
/// Validates the transaction against the current state and returns the resulting diff
|
||||
/// without applying it. Rejects transactions that modify clock system accounts and
|
||||
/// rejects unsafe modifications of the system faucet account.
|
||||
/// rejects unsafe modifications of the system faucet account. Also rejects direct
|
||||
/// invocation of the faucet program for user-submitted transactions.
|
||||
///
|
||||
/// This check is required for all user transactions. Only sequencer transaction may bypass this
|
||||
/// check.
|
||||
@ -78,6 +79,14 @@ impl NSSATransaction {
|
||||
block_id: BlockId,
|
||||
timestamp: Timestamp,
|
||||
) -> Result<ValidatedStateDiff, nssa::error::NssaError> {
|
||||
if let Self::Public(tx) = self
|
||||
&& tx.message().program_id == nssa::program::Program::faucet().id()
|
||||
{
|
||||
return Err(nssa::error::NssaError::InvalidInput(
|
||||
"Transaction invokes restricted faucet program".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let diff = match self {
|
||||
Self::Public(tx) => {
|
||||
ValidatedStateDiff::from_public_transaction(tx, state, block_id, timestamp)
|
||||
@ -102,36 +111,6 @@ impl NSSATransaction {
|
||||
));
|
||||
}
|
||||
|
||||
let faucet_account_id = nssa::SYSTEM_FAUCET_ACCOUNT_ID;
|
||||
if let Some(post_faucet) = public_diff.get(&faucet_account_id) {
|
||||
let pre_faucet = state.get_account_by_id(faucet_account_id);
|
||||
|
||||
let nssa::Account {
|
||||
program_owner: post_program_owner,
|
||||
data: post_data,
|
||||
nonce: post_nonce,
|
||||
balance: post_balance,
|
||||
} = post_faucet;
|
||||
|
||||
let nssa::Account {
|
||||
program_owner: pre_program_owner,
|
||||
data: pre_data,
|
||||
nonce: pre_nonce,
|
||||
balance: pre_balance,
|
||||
} = pre_faucet;
|
||||
|
||||
let faucet_change_is_allowed = *post_program_owner == pre_program_owner
|
||||
&& *post_data == pre_data
|
||||
&& *post_nonce == pre_nonce
|
||||
&& *post_balance >= pre_balance;
|
||||
|
||||
if !faucet_change_is_allowed {
|
||||
return Err(nssa::error::NssaError::InvalidInput(
|
||||
"Transaction modifies system faucet account".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(diff)
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ serde_json.workspace = true
|
||||
token_core.workspace = true
|
||||
ata_core.workspace = true
|
||||
vault_core.workspace = true
|
||||
faucet_core.workspace = true
|
||||
indexer_service_rpc = { workspace = true, features = ["client"] }
|
||||
sequencer_service_rpc = { workspace = true, features = ["client"] }
|
||||
jsonrpsee = { workspace = true, features = ["ws-client"] }
|
||||
|
||||
@ -4,7 +4,7 @@ use anyhow::Result;
|
||||
use common::transaction::NSSATransaction;
|
||||
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, public_mention};
|
||||
use log::info;
|
||||
use nssa::{SYSTEM_FAUCET_ACCOUNT_ID, program::Program, public_transaction};
|
||||
use nssa::{program::Program, public_transaction, system_faucet_account_id};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
use wallet::{
|
||||
@ -349,6 +349,7 @@ async fn successful_transfer_using_to_label() -> Result<()> {
|
||||
#[test]
|
||||
async fn cannot_transfer_funds_from_system_faucet_account() -> Result<()> {
|
||||
let ctx = TestContext::new().await?;
|
||||
let faucet_account_id = system_faucet_account_id();
|
||||
|
||||
let recipient = ctx.existing_public_accounts()[0];
|
||||
let recipient_balance_before = ctx
|
||||
@ -357,13 +358,13 @@ async fn cannot_transfer_funds_from_system_faucet_account() -> Result<()> {
|
||||
.await?;
|
||||
let faucet_balance_before = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(SYSTEM_FAUCET_ACCOUNT_ID)
|
||||
.get_account_balance(faucet_account_id)
|
||||
.await?;
|
||||
|
||||
let amount = 1_u128;
|
||||
let message = public_transaction::Message::try_new(
|
||||
Program::authenticated_transfer_program().id(),
|
||||
vec![SYSTEM_FAUCET_ACCOUNT_ID, recipient],
|
||||
vec![faucet_account_id, recipient],
|
||||
vec![],
|
||||
authenticated_transfer_core::Instruction::Transfer { amount },
|
||||
)?;
|
||||
@ -385,7 +386,7 @@ async fn cannot_transfer_funds_from_system_faucet_account() -> Result<()> {
|
||||
.await?;
|
||||
let faucet_balance_after = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(SYSTEM_FAUCET_ACCOUNT_ID)
|
||||
.get_account_balance(faucet_account_id)
|
||||
.await?;
|
||||
let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?;
|
||||
|
||||
@ -399,18 +400,19 @@ async fn cannot_transfer_funds_from_system_faucet_account() -> Result<()> {
|
||||
#[test]
|
||||
async fn can_transfer_funds_to_system_faucet_account() -> Result<()> {
|
||||
let mut ctx = TestContext::new().await?;
|
||||
let faucet_account_id = system_faucet_account_id();
|
||||
|
||||
let sender = ctx.existing_public_accounts()[0];
|
||||
let sender_balance_before = ctx.sequencer_client().get_account_balance(sender).await?;
|
||||
let faucet_balance_before = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(SYSTEM_FAUCET_ACCOUNT_ID)
|
||||
.get_account_balance(faucet_account_id)
|
||||
.await?;
|
||||
|
||||
let amount = 100_u128;
|
||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||
from: public_mention(sender),
|
||||
to: Some(public_mention(SYSTEM_FAUCET_ACCOUNT_ID)),
|
||||
to: Some(public_mention(faucet_account_id)),
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
@ -424,7 +426,7 @@ async fn can_transfer_funds_to_system_faucet_account() -> Result<()> {
|
||||
let sender_balance_after = ctx.sequencer_client().get_account_balance(sender).await?;
|
||||
let faucet_balance_after = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(SYSTEM_FAUCET_ACCOUNT_ID)
|
||||
.get_account_balance(faucet_account_id)
|
||||
.await?;
|
||||
|
||||
assert_eq!(sender_balance_after, sender_balance_before - amount);
|
||||
@ -432,3 +434,61 @@ async fn can_transfer_funds_to_system_faucet_account() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn cannot_execute_faucet_program() -> Result<()> {
|
||||
let ctx = TestContext::new().await?;
|
||||
let faucet_account_id = system_faucet_account_id();
|
||||
|
||||
let recipient = ctx.existing_public_accounts()[0];
|
||||
let vault_program_id = Program::vault().id();
|
||||
let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient);
|
||||
|
||||
let recipient_balance_before = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(recipient)
|
||||
.await?;
|
||||
let faucet_balance_before = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(faucet_account_id)
|
||||
.await?;
|
||||
|
||||
let amount = 1_u128;
|
||||
let message = public_transaction::Message::try_new(
|
||||
Program::faucet().id(),
|
||||
vec![faucet_account_id, recipient_vault_id],
|
||||
vec![],
|
||||
faucet_core::Instruction::Transfer {
|
||||
vault_program_id,
|
||||
recipient_id: recipient,
|
||||
amount,
|
||||
},
|
||||
)?;
|
||||
let tx = nssa::PublicTransaction::new(
|
||||
message,
|
||||
nssa::public_transaction::WitnessSet::from_raw_parts(vec![]),
|
||||
);
|
||||
let tx_hash = ctx
|
||||
.sequencer_client()
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?;
|
||||
|
||||
info!("Waiting for next block creation");
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
let recipient_balance_after = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(recipient)
|
||||
.await?;
|
||||
let faucet_balance_after = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(faucet_account_id)
|
||||
.await?;
|
||||
let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?;
|
||||
|
||||
assert_eq!(recipient_balance_after, recipient_balance_before);
|
||||
assert_eq!(faucet_balance_after, faucet_balance_before);
|
||||
assert!(tx_on_chain.is_none());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ workspace = true
|
||||
[dependencies]
|
||||
nssa_core = { workspace = true, features = ["host"] }
|
||||
clock_core.workspace = true
|
||||
faucet_core.workspace = true
|
||||
|
||||
anyhow.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
@ -26,8 +26,6 @@ pub mod program;
|
||||
pub mod error;
|
||||
|
||||
pub const GENESIS_BLOCK_ID: BlockId = 1;
|
||||
pub const SYSTEM_FAUCET_ACCOUNT_ID: account::AccountId =
|
||||
account::AccountId::new(*b"/LEZ/SystemFaucetAccount/0000000");
|
||||
|
||||
pub type BlockId = u64;
|
||||
/// Unix timestamp in milliseconds.
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
)]
|
||||
|
||||
pub use nssa_core::{
|
||||
GENESIS_BLOCK_ID, SYSTEM_FAUCET_ACCOUNT_ID, SharedSecretKey,
|
||||
GENESIS_BLOCK_ID, SharedSecretKey,
|
||||
account::{Account, AccountId, Data},
|
||||
encryption::EphemeralPublicKey,
|
||||
program::ProgramId,
|
||||
@ -18,7 +18,7 @@ pub use public_transaction::PublicTransaction;
|
||||
pub use signature::{PrivateKey, PublicKey, Signature};
|
||||
pub use state::{
|
||||
CLOCK_01_PROGRAM_ACCOUNT_ID, CLOCK_10_PROGRAM_ACCOUNT_ID, CLOCK_50_PROGRAM_ACCOUNT_ID,
|
||||
CLOCK_PROGRAM_ACCOUNT_IDS, V03State,
|
||||
CLOCK_PROGRAM_ACCOUNT_IDS, V03State, system_faucet_account_id,
|
||||
};
|
||||
pub use validated_state_diff::ValidatedStateDiff;
|
||||
|
||||
|
||||
@ -10,8 +10,8 @@ use crate::{
|
||||
error::NssaError,
|
||||
program_methods::{
|
||||
AMM_ELF, AMM_ID, ASSOCIATED_TOKEN_ACCOUNT_ELF, ASSOCIATED_TOKEN_ACCOUNT_ID,
|
||||
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, CLOCK_ELF, CLOCK_ID, PINATA_ELF,
|
||||
PINATA_ID, TOKEN_ELF, TOKEN_ID, VAULT_ELF, VAULT_ID,
|
||||
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, CLOCK_ELF, CLOCK_ID, FAUCET_ELF,
|
||||
FAUCET_ID, PINATA_ELF, PINATA_ID, TOKEN_ELF, TOKEN_ID, VAULT_ELF, VAULT_ID,
|
||||
},
|
||||
};
|
||||
|
||||
@ -156,6 +156,14 @@ impl Program {
|
||||
elf: VAULT_ELF.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn faucet() -> Self {
|
||||
Self {
|
||||
id: FAUCET_ID,
|
||||
elf: FAUCET_ELF.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Testnet only. Refactor to prevent compilation on mainnet.
|
||||
@ -186,8 +194,9 @@ mod tests {
|
||||
program::Program,
|
||||
program_methods::{
|
||||
AMM_ELF, AMM_ID, ASSOCIATED_TOKEN_ACCOUNT_ELF, ASSOCIATED_TOKEN_ACCOUNT_ID,
|
||||
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, CLOCK_ELF, CLOCK_ID, PINATA_ELF,
|
||||
PINATA_ID, PINATA_TOKEN_ELF, PINATA_TOKEN_ID, TOKEN_ELF, TOKEN_ID, VAULT_ELF, VAULT_ID,
|
||||
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, CLOCK_ELF, CLOCK_ID, FAUCET_ELF,
|
||||
FAUCET_ID, PINATA_ELF, PINATA_ID, PINATA_TOKEN_ELF, PINATA_TOKEN_ID, TOKEN_ELF,
|
||||
TOKEN_ID, VAULT_ELF, VAULT_ID,
|
||||
},
|
||||
};
|
||||
|
||||
@ -501,6 +510,7 @@ mod tests {
|
||||
let auth_transfer_program = Program::authenticated_transfer_program();
|
||||
let token_program = Program::token();
|
||||
let vault_program = Program::vault();
|
||||
let faucet_program = Program::faucet();
|
||||
let pinata_program = Program::pinata();
|
||||
|
||||
assert_eq!(auth_transfer_program.id, AUTHENTICATED_TRANSFER_ID);
|
||||
@ -509,6 +519,8 @@ mod tests {
|
||||
assert_eq!(token_program.elf, TOKEN_ELF);
|
||||
assert_eq!(vault_program.id, VAULT_ID);
|
||||
assert_eq!(vault_program.elf, VAULT_ELF);
|
||||
assert_eq!(faucet_program.id, FAUCET_ID);
|
||||
assert_eq!(faucet_program.elf, FAUCET_ELF);
|
||||
assert_eq!(pinata_program.id, PINATA_ID);
|
||||
assert_eq!(pinata_program.elf, PINATA_ELF);
|
||||
}
|
||||
@ -520,6 +532,7 @@ mod tests {
|
||||
(AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID),
|
||||
(ASSOCIATED_TOKEN_ACCOUNT_ELF, ASSOCIATED_TOKEN_ACCOUNT_ID),
|
||||
(CLOCK_ELF, CLOCK_ID),
|
||||
(FAUCET_ELF, FAUCET_ID),
|
||||
(PINATA_ELF, PINATA_ID),
|
||||
(PINATA_TOKEN_ELF, PINATA_TOKEN_ID),
|
||||
(TOKEN_ELF, TOKEN_ID),
|
||||
|
||||
@ -8,7 +8,7 @@ pub use clock_core::{
|
||||
};
|
||||
use nssa_core::{
|
||||
BlockId, Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, MembershipProof, Nullifier,
|
||||
SYSTEM_FAUCET_ACCOUNT_ID, Timestamp,
|
||||
Timestamp,
|
||||
account::{Account, AccountId, Nonce},
|
||||
program::ProgramId,
|
||||
};
|
||||
@ -124,9 +124,10 @@ pub struct V03State {
|
||||
|
||||
impl Default for V03State {
|
||||
fn default() -> Self {
|
||||
let faucet_account_id = system_faucet_account_id();
|
||||
let faucet_account = system_faucet_account();
|
||||
let mut public_state = HashMap::new();
|
||||
public_state.insert(SYSTEM_FAUCET_ACCOUNT_ID, faucet_account);
|
||||
public_state.insert(faucet_account_id, faucet_account);
|
||||
|
||||
Self {
|
||||
public_state,
|
||||
@ -148,6 +149,7 @@ impl V03State {
|
||||
initial_private_accounts: Vec<(Commitment, Nullifier)>,
|
||||
genesis_timestamp: nssa_core::Timestamp,
|
||||
) -> Self {
|
||||
let faucet_account_id = system_faucet_account_id();
|
||||
let authenticated_transfer_program = Program::authenticated_transfer_program();
|
||||
let mut public_state: HashMap<_, _> = initial_data
|
||||
.iter()
|
||||
@ -162,7 +164,7 @@ impl V03State {
|
||||
})
|
||||
.collect();
|
||||
let faucet_account = system_faucet_account();
|
||||
public_state.insert(SYSTEM_FAUCET_ACCOUNT_ID, faucet_account);
|
||||
public_state.insert(faucet_account_id, faucet_account);
|
||||
|
||||
let mut commitment_set = CommitmentSet::with_capacity(32);
|
||||
commitment_set.extend(&[DUMMY_COMMITMENT]);
|
||||
@ -187,6 +189,7 @@ impl V03State {
|
||||
this.insert_program(Program::amm());
|
||||
this.insert_program(Program::ata());
|
||||
this.insert_program(Program::vault());
|
||||
this.insert_program(Program::faucet());
|
||||
|
||||
this
|
||||
}
|
||||
@ -381,6 +384,11 @@ fn system_faucet_account() -> Account {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn system_faucet_account_id() -> AccountId {
|
||||
faucet_core::compute_faucet_account_id(Program::faucet().id())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
#![expect(
|
||||
@ -404,7 +412,7 @@ pub mod tests {
|
||||
};
|
||||
|
||||
use crate::{
|
||||
PublicKey, PublicTransaction, SYSTEM_FAUCET_ACCOUNT_ID, V03State,
|
||||
PublicKey, PublicTransaction, V03State,
|
||||
error::{InvalidProgramBehaviorError, NssaError},
|
||||
execute_and_prove,
|
||||
privacy_preserving_transaction::{
|
||||
@ -420,6 +428,7 @@ pub mod tests {
|
||||
CLOCK_01_PROGRAM_ACCOUNT_ID, CLOCK_10_PROGRAM_ACCOUNT_ID, CLOCK_50_PROGRAM_ACCOUNT_ID,
|
||||
CLOCK_PROGRAM_ACCOUNT_IDS, MAX_NUMBER_CHAINED_CALLS, system_faucet_account,
|
||||
},
|
||||
system_faucet_account_id,
|
||||
};
|
||||
|
||||
impl V03State {
|
||||
@ -612,7 +621,7 @@ pub mod tests {
|
||||
..Account::default()
|
||||
},
|
||||
);
|
||||
this.insert(SYSTEM_FAUCET_ACCOUNT_ID, system_faucet_account());
|
||||
this.insert(system_faucet_account_id(), system_faucet_account());
|
||||
for account_id in CLOCK_PROGRAM_ACCOUNT_IDS {
|
||||
this.insert(
|
||||
account_id,
|
||||
@ -636,6 +645,7 @@ pub mod tests {
|
||||
this.insert(Program::amm().id(), Program::amm());
|
||||
this.insert(Program::ata().id(), Program::ata());
|
||||
this.insert(Program::vault().id(), Program::vault());
|
||||
this.insert(Program::faucet().id(), Program::faucet());
|
||||
this
|
||||
};
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ amm_core.workspace = true
|
||||
amm_program.workspace = true
|
||||
ata_core.workspace = true
|
||||
ata_program.workspace = true
|
||||
faucet_core.workspace = true
|
||||
vault_core.workspace = true
|
||||
risc0-zkvm.workspace = true
|
||||
serde = { workspace = true, default-features = false }
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use authenticated_transfer_core::Instruction;
|
||||
use nssa_core::{
|
||||
SYSTEM_FAUCET_ACCOUNT_ID,
|
||||
account::{Account, AccountWithMetadata},
|
||||
program::{
|
||||
AccountPostState, Claim, DEFAULT_PROGRAM_ID, ProgramInput, ProgramOutput, read_nssa_inputs,
|
||||
@ -26,13 +25,8 @@ fn transfer(
|
||||
recipient: AccountWithMetadata,
|
||||
balance_to_move: u128,
|
||||
) -> Vec<AccountPostState> {
|
||||
// Continue only if the sender has authorized this operation
|
||||
// or it's the system faucet account which is allowed without authorization as it may be used
|
||||
// only by sequencer.
|
||||
assert!(
|
||||
sender.is_authorized || sender.account_id == SYSTEM_FAUCET_ACCOUNT_ID,
|
||||
"Sender must be authorized"
|
||||
);
|
||||
// Continue only if the sender has authorized this operation.
|
||||
assert!(sender.is_authorized, "Sender must be authorized");
|
||||
|
||||
// Create accounts post states, with updated balances
|
||||
let sender_post = {
|
||||
|
||||
71
program_methods/guest/src/bin/faucet.rs
Normal file
71
program_methods/guest/src/bin/faucet.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use faucet_core::Instruction;
|
||||
use nssa_core::program::{
|
||||
AccountPostState, ChainedCall, ProgramInput, ProgramOutput, read_nssa_inputs,
|
||||
};
|
||||
|
||||
fn unchanged_post_states(
|
||||
pre_states: &[nssa_core::account::AccountWithMetadata],
|
||||
) -> Vec<AccountPostState> {
|
||||
pre_states
|
||||
.iter()
|
||||
.map(|pre_state| AccountPostState::new(pre_state.account.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (
|
||||
ProgramInput {
|
||||
self_program_id,
|
||||
caller_program_id,
|
||||
pre_states,
|
||||
instruction,
|
||||
},
|
||||
instruction_words,
|
||||
) = read_nssa_inputs::<Instruction>();
|
||||
|
||||
let pre_states_clone = pre_states.clone();
|
||||
let post_states = unchanged_post_states(&pre_states_clone);
|
||||
|
||||
let chained_calls = match instruction {
|
||||
Instruction::Transfer {
|
||||
vault_program_id,
|
||||
recipient_id,
|
||||
amount,
|
||||
} => {
|
||||
let [faucet, recipient_vault] = pre_states
|
||||
.try_into()
|
||||
.expect("Transfer requires exactly 2 accounts");
|
||||
|
||||
assert_eq!(
|
||||
faucet.account_id,
|
||||
faucet_core::compute_faucet_account_id(self_program_id),
|
||||
"First account must be faucet PDA"
|
||||
);
|
||||
|
||||
let mut faucet_for_vault = faucet;
|
||||
faucet_for_vault.is_authorized = true;
|
||||
|
||||
vec![
|
||||
ChainedCall::new(
|
||||
vault_program_id,
|
||||
vec![faucet_for_vault, recipient_vault],
|
||||
&vault_core::Instruction::Transfer {
|
||||
recipient_id,
|
||||
amount,
|
||||
},
|
||||
)
|
||||
.with_pda_seeds(vec![faucet_core::compute_faucet_seed()]),
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
ProgramOutput::new(
|
||||
self_program_id,
|
||||
caller_program_id,
|
||||
instruction_words,
|
||||
pre_states_clone,
|
||||
post_states,
|
||||
)
|
||||
.with_chained_calls(chained_calls)
|
||||
.write();
|
||||
}
|
||||
12
programs/faucet/core/Cargo.toml
Normal file
12
programs/faucet/core/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "faucet_core"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
license = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nssa_core.workspace = true
|
||||
serde = { workspace = true, default-features = false }
|
||||
29
programs/faucet/core/src/lib.rs
Normal file
29
programs/faucet/core/src/lib.rs
Normal file
@ -0,0 +1,29 @@
|
||||
pub use nssa_core::program::PdaSeed;
|
||||
use nssa_core::{account::AccountId, program::ProgramId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const FAUCET_SEED_DOMAIN_SEPARATOR: [u8; 32] = *b"/LEZ/v0.3/FaucetSeed/0000000000/";
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum Instruction {
|
||||
/// Transfers native tokens from system faucet to recipient's vault.
|
||||
///
|
||||
/// Required accounts (2):
|
||||
/// - Faucet PDA account
|
||||
/// - Recipient vault PDA account
|
||||
Transfer {
|
||||
vault_program_id: ProgramId,
|
||||
recipient_id: AccountId,
|
||||
amount: u128,
|
||||
},
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn compute_faucet_seed() -> PdaSeed {
|
||||
PdaSeed::new(FAUCET_SEED_DOMAIN_SEPARATOR)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn compute_faucet_account_id(faucet_program_id: ProgramId) -> AccountId {
|
||||
AccountId::for_public_pda(&faucet_program_id, &compute_faucet_seed())
|
||||
}
|
||||
@ -15,6 +15,7 @@ storage.workspace = true
|
||||
mempool.workspace = true
|
||||
logos-blockchain-zone-sdk.workspace = true
|
||||
testnet_initial_state.workspace = true
|
||||
faucet_core.workspace = true
|
||||
vault_core.workspace = true
|
||||
|
||||
anyhow.workspace = true
|
||||
|
||||
@ -380,14 +380,16 @@ fn build_supply_account_genesis_transaction(
|
||||
account_id: &AccountId,
|
||||
balance: u128,
|
||||
) -> PublicTransaction {
|
||||
let faucet_program_id = Program::faucet().id();
|
||||
let vault_program_id = Program::vault().id();
|
||||
let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, *account_id);
|
||||
|
||||
let message = Message::try_new(
|
||||
vault_program_id,
|
||||
vec![nssa::SYSTEM_FAUCET_ACCOUNT_ID, recipient_vault_id],
|
||||
faucet_program_id,
|
||||
vec![nssa::system_faucet_account_id(), recipient_vault_id],
|
||||
vec![],
|
||||
vault_core::Instruction::Transfer {
|
||||
faucet_core::Instruction::Transfer {
|
||||
vault_program_id,
|
||||
recipient_id: *account_id,
|
||||
amount: balance,
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user