Skip to content

Commit 77e3db9

Browse files
Fix fee validation in wasm + update provable.tools
1 parent 5aacde4 commit 77e3db9

File tree

17 files changed

+128
-220
lines changed

17 files changed

+128
-220
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
**/tmp
99
storage*/
1010
**/.next
11+
*.zip
1112

1213
## OS Files
1314
**.DS_Store

sdk/src/network-client.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -752,9 +752,10 @@ class AleoNetworkClient {
752752
* const account = new Account();
753753
* const publicBalance = networkClient.getPublicBalance(account.address());
754754
*/
755-
async getPublicBalance(address: Address): Promise<number> {
755+
async getPublicBalance(address: Address | string): Promise<number> {
756756
try {
757-
const balanceStr = await this.getProgramMappingValue('credits.aleo', 'account', address.to_string());
757+
const addressString = address instanceof Address ? address.to_string() : address;
758+
const balanceStr = await this.getProgramMappingValue('credits.aleo', 'account', addressString);
758759
return balanceStr ? parseInt(balanceStr) : 0;
759760
} catch (error) {
760761
throw new Error(`Error fetching public balance for ${address}: ${error}`);

sdk/src/program-manager.ts

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,11 @@ class ProgramManager {
102102
* Check if the fee is sufficient to pay for the transaction
103103
*/
104104
async checkFee(address: string, feeAmount: bigint) {
105-
const account_balance =
106-
await this.networkClient?.getProgramMappingValue(
107-
"credits.aleo",
108-
"account",
109-
address,
110-
);
111-
if (feeAmount > BigInt(account_balance)) {
105+
const balance =
106+
BigInt(await this.networkClient.getPublicBalance(address));
107+
if (feeAmount > balance) {
112108
throw Error(
113-
"Public balance is insufficient to execute the transacation.",
109+
`The desired execution requires a fee of ${feeAmount} microcredits, but the account paying the fee has ${balance} microcredits available.`,
114110
);
115111
}
116112
}
@@ -198,9 +194,18 @@ class ProgramManager {
198194
feeRecord?: string | RecordPlaintext,
199195
privateKey?: PrivateKey,
200196
): Promise<Transaction> {
197+
// Ensure the program is valid.
198+
let programObject;
199+
try {
200+
programObject = Program.fromString(program);
201+
} catch (e: any) {
202+
logAndThrow(
203+
`Error parsing program: '${e.message}'. Please ensure the program is valid.`,
204+
);
205+
}
206+
201207
// Ensure the program is valid and does not exist on the network
202208
try {
203-
const programObject = Program.fromString(program);
204209
let programSource;
205210
try {
206211
programSource = await this.networkClient.getProgram(
@@ -212,8 +217,8 @@ class ProgramManager {
212217
`Program ${programObject.id()} does not exist on the network, deploying...`,
213218
);
214219
}
215-
if (typeof programSource == "string") {
216-
throw `Program ${programObject.id()} already exists on the network, please rename your program`;
220+
if (typeof programSource === "string") {
221+
throw Error(`Program ${programObject.id()} already exists on the network, please rename your program`);
217222
}
218223
} catch (e: any) {
219224
logAndThrow(`Error validating program: ${e.message}`);
@@ -354,7 +359,7 @@ class ProgramManager {
354359
}
355360

356361
// Check if the account has sufficient credits to pay for the transaction
357-
this.checkFee(feeAddress.to_string(), tx.feeAmount());
362+
await this.checkFee(feeAddress.to_string(), tx.feeAmount());
358363

359364
return await this.networkClient.submitTransaction(tx);
360365
}
@@ -572,7 +577,7 @@ class ProgramManager {
572577
}
573578

574579
// Check if the account has sufficient credits to pay for the transaction
575-
this.checkFee(feeAddress.to_string(), tx.feeAmount());
580+
await this.checkFee(feeAddress.to_string(), tx.feeAmount());
576581

577582
return await this.networkClient.submitTransaction(tx);
578583
}
@@ -793,7 +798,7 @@ class ProgramManager {
793798
);
794799

795800
// Check if the account has sufficient credits to pay for the transaction
796-
this.checkFee(feeAddress.to_string(), tx.feeAmount());
801+
await this.checkFee(feeAddress.to_string(), tx.feeAmount());
797802

798803
return await this.networkClient.submitTransaction(tx);
799804
}
@@ -1241,7 +1246,7 @@ class ProgramManager {
12411246
}
12421247

12431248
// Check if the account has sufficient credits to pay for the transaction
1244-
this.checkFee(feeAddress.to_string(), tx.feeAmount());
1249+
await this.checkFee(feeAddress.to_string(), tx.feeAmount());
12451250

12461251
return await this.networkClient.submitTransaction(tx);
12471252
}
@@ -1376,7 +1381,7 @@ class ProgramManager {
13761381
}
13771382

13781383
// Check if the account has sufficient credits to pay for the transaction
1379-
this.checkFee(feeAddress.to_string(), tx.feeAmount());
1384+
await this.checkFee(feeAddress.to_string(), tx.feeAmount());
13801385

13811386
return await this.networkClient.submitTransaction(tx);
13821387
}
@@ -1520,7 +1525,7 @@ class ProgramManager {
15201525
}
15211526

15221527
// Check if the account has sufficient credits to pay for the transaction
1523-
this.checkFee(feeAddress.to_string(), tx.feeAmount());
1528+
await this.checkFee(feeAddress.to_string(), tx.feeAmount());
15241529

15251530
return await this.networkClient.submitTransaction(tx);
15261531
}
@@ -1649,7 +1654,7 @@ class ProgramManager {
16491654
}
16501655

16511656
// Check if the account has sufficient credits to pay for the transaction
1652-
this.checkFee(feeAddress.to_string(), tx.feeAmount());
1657+
await this.checkFee(feeAddress.to_string(), tx.feeAmount());
16531658

16541659
return await this.networkClient.submitTransaction(tx);
16551660
}
@@ -1770,7 +1775,7 @@ class ProgramManager {
17701775
}
17711776

17721777
// Check if the account has sufficient credits to pay for the transaction
1773-
this.checkFee(feeAddress.to_string(), tx.feeAmount());
1778+
await this.checkFee(feeAddress.to_string(), tx.feeAmount());
17741779

17751780
return await this.networkClient.submitTransaction(tx);
17761781
}
@@ -1903,7 +1908,7 @@ class ProgramManager {
19031908
}
19041909

19051910
// Check if the account has sufficient credits to pay for the transaction
1906-
this.checkFee(feeAddress.to_string(), tx.feeAmount());
1911+
await this.checkFee(feeAddress.to_string(), tx.feeAmount());
19071912

19081913
return this.networkClient.submitTransaction(tx);
19091914
}

wasm/src/programs/macros.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ macro_rules! execute_program {
9898

9999
#[macro_export]
100100
macro_rules! execute_fee {
101-
($process:expr, $private_key:expr, $fee_record:expr, $fee_microcredits:expr, $submission_url:expr, $fee_proving_key:expr, $fee_verifying_key:expr, $execution_id:expr, $rng:expr, $offline_query:expr, $minimum_execution_cost:expr) => {{
101+
($process:expr, $private_key:expr, $fee_record:expr, $priority_fee_microcredits:expr, $submission_url:expr, $fee_proving_key:expr, $fee_verifying_key:expr, $execution_id:expr, $rng:expr, $offline_query:expr, $minimum_execution_cost:expr) => {{
102102
if (($fee_proving_key.is_some() && $fee_verifying_key.is_none())
103103
|| ($fee_proving_key.is_none() && $fee_verifying_key.is_some()))
104104
{
@@ -137,7 +137,7 @@ macro_rules! execute_fee {
137137
$private_key,
138138
fee_record_native,
139139
$minimum_execution_cost,
140-
$fee_microcredits,
140+
$priority_fee_microcredits,
141141
$execution_id,
142142
$rng,
143143
).map_err(|e| e.to_string())?
@@ -146,7 +146,7 @@ macro_rules! execute_fee {
146146
$process.authorize_fee_public::<CurrentAleo, _>(
147147
$private_key,
148148
$minimum_execution_cost,
149-
$fee_microcredits,
149+
$priority_fee_microcredits,
150150
$execution_id,
151151
$rng,
152152
).map_err(|e| e.to_string())?

wasm/src/programs/manager/deploy.rs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,6 @@ impl ProgramManager {
7373
offline_query: Option<OfflineQuery>,
7474
) -> Result<Transaction, String> {
7575
log("Creating deployment transaction");
76-
// Convert fee to microcredits and check that the fee record has enough credits to pay it
77-
let fee_microcredits = match &fee_record {
78-
Some(fee_record) => Self::validate_amount(priority_fee_credits, fee_record, true)?,
79-
None => (priority_fee_credits * 1_000_000.0) as u64,
80-
};
81-
8276
let mut process_native = ProcessNative::load_web().map_err(|err| err.to_string())?;
8377
let process = &mut process_native;
8478

@@ -99,20 +93,18 @@ impl ProgramManager {
9993
log("Ensuring the fee is sufficient to pay for the deployment");
10094
let (minimum_deployment_cost, (_, _, _)) =
10195
deployment_cost::<CurrentNetwork>(&deployment).map_err(|err| err.to_string())?;
102-
if fee_microcredits < minimum_deployment_cost {
103-
return Err(format!(
104-
"Fee is too low to pay for the deployment. The minimum fee is {} credits",
105-
minimum_deployment_cost as f64 / 1_000_000.0
106-
));
107-
}
96+
97+
// Check to see if the fee record has enough microcredits to pay for the deployment.
98+
let priority_fee_microcredits = (priority_fee_credits * 1_000_000.0) as u64;
99+
Self::validate_fee_record(&fee_record, minimum_deployment_cost, priority_fee_microcredits)?;
108100

109101
let deployment_id = deployment.to_deployment_id().map_err(|e| e.to_string())?;
110102

111103
let fee = execute_fee!(
112104
process,
113105
private_key,
114106
fee_record,
115-
fee_microcredits,
107+
priority_fee_microcredits,
116108
node_url,
117109
fee_proving_key,
118110
fee_verifying_key,

wasm/src/programs/manager/execute.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,6 @@ impl ProgramManager {
169169
offline_query: Option<OfflineQuery>,
170170
) -> Result<Transaction, String> {
171171
log(&format!("Executing function: {function} on-chain"));
172-
let fee_microcredits = match &fee_record {
173-
Some(fee_record) => Self::validate_amount(priority_fee_credits, fee_record, true)?,
174-
None => (priority_fee_credits * 1_000_000.0) as u64,
175-
};
176172
let mut process_native = ProcessNative::load_web().map_err(|err| err.to_string())?;
177173
let process = &mut process_native;
178174
let node_url = url.as_deref().unwrap_or(DEFAULT_URL);
@@ -213,12 +209,16 @@ impl ProgramManager {
213209
log("Calculating the minimum execution fee");
214210
let minimum_execution_cost = calculate_minimum_fee!(offline_query, node_url, process, &execution);
215211

212+
// Check to see if the fee record has enough microcredits to pay for the deployment.
213+
let priority_fee_microcredits = (priority_fee_credits * 1_000_000.0) as u64;
214+
Self::validate_fee_record(&fee_record, minimum_execution_cost, priority_fee_microcredits)?;
215+
216216
log("Executing fee");
217217
let fee = execute_fee!(
218218
process,
219219
private_key,
220220
fee_record,
221-
fee_microcredits,
221+
priority_fee_microcredits,
222222
node_url,
223223
fee_proving_key,
224224
fee_verifying_key,

wasm/src/programs/manager/join.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use crate::{
3838
};
3939
use snarkvm_algorithms::snark::varuna::VarunaVersion;
4040
use snarkvm_console::prelude::{ConsensusVersion, Network};
41-
use snarkvm_ledger_query::{Query, QueryTrait};
41+
use snarkvm_ledger_query::QueryTrait;
4242
use snarkvm_synthesizer::prelude::{execution_cost_v1, execution_cost_v2};
4343
use snarkvm_synthesizer_program::StackKeys;
4444

@@ -78,10 +78,6 @@ impl ProgramManager {
7878
offline_query: Option<OfflineQuery>,
7979
) -> Result<Transaction, String> {
8080
log("Executing join program");
81-
let fee_microcredits = match &fee_record {
82-
Some(fee_record) => Self::validate_amount(priority_fee_credits, fee_record, true)?,
83-
None => (priority_fee_credits * 1_000_000.0) as u64,
84-
};
8581
let rng = &mut StdRng::from_entropy();
8682

8783
log("Setup program and inputs");
@@ -144,12 +140,16 @@ impl ProgramManager {
144140
log("Calculating the minimum execution fee");
145141
let minimum_execution_cost = calculate_minimum_fee!(offline_query, node_url, process, &execution);
146142

143+
// Check to see if the fee record has enough microcredits to pay for the deployment.
144+
let priority_fee_microcredits = (priority_fee_credits * 1_000_000.0) as u64;
145+
Self::validate_fee_record(&fee_record, minimum_execution_cost, priority_fee_microcredits)?;
146+
147147
log("Executing the fee");
148148
let fee = execute_fee!(
149149
process,
150150
private_key,
151151
fee_record,
152-
fee_microcredits,
152+
priority_fee_microcredits,
153153
node_url,
154154
fee_proving_key,
155155
fee_verifying_key,

wasm/src/programs/manager/mod.rs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use crate::{
2828
ProvingKey,
2929
RecordPlaintext,
3030
VerifyingKey,
31+
log,
3132
types::native::{
3233
IdentifierNative,
3334
ProcessNative,
@@ -52,22 +53,6 @@ pub struct ProgramManager;
5253

5354
#[wasm_bindgen]
5455
impl ProgramManager {
55-
/// Validate that an amount being paid from a record is greater than zero and that the record
56-
/// has enough credits to pay the amount
57-
pub(crate) fn validate_amount(credits: f64, amount: &RecordPlaintext, fee: bool) -> Result<u64, String> {
58-
let name = if fee { "Fee" } else { "Amount" };
59-
60-
if credits <= 0.0 {
61-
return Err(format!("{name} must be greater than zero to deploy or execute a program"));
62-
}
63-
let microcredits = (credits * 1_000_000.0f64) as u64;
64-
if amount.microcredits() < microcredits {
65-
return Err(format!("{name} record does not have enough credits to pay the specified fee"));
66-
}
67-
68-
Ok(microcredits)
69-
}
70-
7156
/// Synthesize proving and verifying keys for a program
7257
///
7358
/// @param program {string} The program source code of the program to synthesize keys for
@@ -142,6 +127,26 @@ impl ProgramManager {
142127
Ok(())
143128
}
144129
}
130+
131+
pub(crate) fn validate_fee_record(
132+
fee_record: &Option<RecordPlaintext>,
133+
minimum_execution_cost: u64,
134+
priority_fee_microcredits: u64,
135+
) -> Result<(), String> {
136+
let total_fee = priority_fee_microcredits.saturating_add(minimum_execution_cost);
137+
if let Some(fee_record) = fee_record {
138+
log("Validating the fee record");
139+
if fee_record.microcredits() < total_fee {
140+
return Err(format!(
141+
"Fee record does not have enough credits to pay for a fee of {} credits. (base fee: {} credits - priority fee: {} credits)",
142+
total_fee as f64 / 1_000_000.0,
143+
minimum_execution_cost as f64 / 1_000_000.0,
144+
priority_fee_microcredits as f64 / 1_000_000.0,
145+
));
146+
}
147+
}
148+
Ok(())
149+
}
145150
}
146151

147152
#[cfg(test)]

wasm/src/programs/manager/split.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ impl ProgramManager {
5555
offline_query: Option<OfflineQuery>,
5656
) -> Result<Transaction, String> {
5757
log("Executing split program");
58-
let amount_microcredits = Self::validate_amount(split_amount, &amount_record, false)?;
58+
let amount_microcredits = (split_amount * 1_000_000.0) as u64;
59+
if amount_microcredits > amount_record.microcredits() {
60+
return Err("Amount record does not have enough credits".to_string());
61+
}
5962

6063
log("Setup the program and inputs");
6164
let node_url = url.as_deref().unwrap_or(DEFAULT_URL);

0 commit comments

Comments
 (0)