Do Budget verification in BudgetScript
This commit is contained in:
parent
ce6257a069
commit
a54854abc7
|
@ -18,6 +18,9 @@ impl BudgetScript {
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
expr: BudgetExpr,
|
expr: BudgetExpr,
|
||||||
) -> Script {
|
) -> Script {
|
||||||
|
if !expr.verify(lamports) {
|
||||||
|
panic!("invalid budget expression");
|
||||||
|
}
|
||||||
let space = serialized_size(&BudgetState::new(expr.clone())).unwrap();
|
let space = serialized_size(&BudgetState::new(expr.clone())).unwrap();
|
||||||
let instructions = vec![
|
let instructions = vec![
|
||||||
SystemInstruction::new_program_account(&from, contract, lamports, space, &id()),
|
SystemInstruction::new_program_account(&from, contract, lamports, space, &id()),
|
||||||
|
@ -70,3 +73,36 @@ impl BudgetScript {
|
||||||
Self::new_account(from, contract, lamports, expr)
|
Self::new_account(from, contract, lamports, expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::budget_expr::BudgetExpr;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_budget_script_verify() {
|
||||||
|
let alice_pubkey = Keypair::new().pubkey();
|
||||||
|
let bob_pubkey = Keypair::new().pubkey();
|
||||||
|
BudgetScript::pay(&alice_pubkey, &bob_pubkey, 1); // No panic! indicates success.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_budget_script_overspend() {
|
||||||
|
let alice_pubkey = Keypair::new().pubkey();
|
||||||
|
let bob_pubkey = Keypair::new().pubkey();
|
||||||
|
let budget_pubkey = Keypair::new().pubkey();
|
||||||
|
let expr = BudgetExpr::new_payment(2, &bob_pubkey);
|
||||||
|
BudgetScript::new_account(&alice_pubkey, &budget_pubkey, 1, expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_budget_script_underspend() {
|
||||||
|
let alice_pubkey = Keypair::new().pubkey();
|
||||||
|
let bob_pubkey = Keypair::new().pubkey();
|
||||||
|
let budget_pubkey = Keypair::new().pubkey();
|
||||||
|
let expr = BudgetExpr::new_payment(1, &bob_pubkey);
|
||||||
|
BudgetScript::new_account(&alice_pubkey, &budget_pubkey, 2, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -109,71 +109,3 @@ impl BudgetTransaction {
|
||||||
Self::new_signed(from_keypair, script, recent_blockhash, 0)
|
Self::new_signed(from_keypair, script, recent_blockhash, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::budget_expr::BudgetExpr;
|
|
||||||
use bincode::{deserialize, serialize};
|
|
||||||
use solana_sdk::system_instruction::SystemInstruction;
|
|
||||||
|
|
||||||
fn deserialize_system_instruction(tx: &Transaction, index: usize) -> Option<SystemInstruction> {
|
|
||||||
deserialize(&tx.data(index)).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_budget_instruction(tx: &Transaction, index: usize) -> Option<BudgetInstruction> {
|
|
||||||
deserialize(&tx.data(index)).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify only the payment plan.
|
|
||||||
fn verify_plan(tx: &Transaction) -> bool {
|
|
||||||
if let Some(SystemInstruction::CreateAccount { lamports, .. }) =
|
|
||||||
deserialize_system_instruction(tx, 0)
|
|
||||||
{
|
|
||||||
if let Some(BudgetInstruction::InitializeAccount(expr)) =
|
|
||||||
deserialize_budget_instruction(&tx, 1)
|
|
||||||
{
|
|
||||||
if !expr.verify(lamports) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_payment() {
|
|
||||||
let zero = Hash::default();
|
|
||||||
let keypair0 = Keypair::new();
|
|
||||||
let keypair1 = Keypair::new();
|
|
||||||
let pubkey1 = keypair1.pubkey();
|
|
||||||
let tx0 = BudgetTransaction::new_payment(&keypair0, &pubkey1, 42, zero, 0);
|
|
||||||
assert!(verify_plan(&tx0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_overspend_attack() {
|
|
||||||
let keypair0 = Keypair::new();
|
|
||||||
let keypair1 = Keypair::new();
|
|
||||||
let zero = Hash::default();
|
|
||||||
let mut tx = BudgetTransaction::new_payment(&keypair0, &keypair1.pubkey(), 1, zero, 0);
|
|
||||||
let mut instruction = deserialize_budget_instruction(&tx, 1).unwrap();
|
|
||||||
if let BudgetInstruction::InitializeAccount(ref mut expr) = instruction {
|
|
||||||
if let BudgetExpr::Pay(ref mut payment) = expr {
|
|
||||||
payment.lamports = 2; // <-- attack!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tx.instructions[1].data = serialize(&instruction).unwrap();
|
|
||||||
assert!(!verify_plan(&tx));
|
|
||||||
|
|
||||||
// Also, ensure all branchs of the plan spend all lamports
|
|
||||||
let mut instruction = deserialize_budget_instruction(&tx, 1).unwrap();
|
|
||||||
if let BudgetInstruction::InitializeAccount(ref mut expr) = instruction {
|
|
||||||
if let BudgetExpr::Pay(ref mut payment) = expr {
|
|
||||||
payment.lamports = 0; // <-- whoops!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tx.instructions[1].data = serialize(&instruction).unwrap();
|
|
||||||
assert!(!verify_plan(&tx));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue