Rename Budget to BudgetExpr
This commit is contained in:
parent
a264f8fa9b
commit
c8c255ad73
|
@ -1,4 +1,4 @@
|
||||||
//! The `budget` module provides a domain-specific language for payment plans. Users create Budget objects that
|
//! The `budget_expr` module provides a domain-specific language for payment plans. Users create BudgetExpr objects that
|
||||||
//! are given to an interpreter. The interpreter listens for `Witness` transactions,
|
//! are given to an interpreter. The interpreter listens for `Witness` transactions,
|
||||||
//! which it uses to reduce the payment plan. When the budget is reduced to a
|
//! which it uses to reduce the payment plan. When the budget is reduced to a
|
||||||
//! `Payment`, the payment is executed.
|
//! `Payment`, the payment is executed.
|
||||||
|
@ -34,7 +34,7 @@ impl Condition {
|
||||||
/// A data type representing a payment plan.
|
/// A data type representing a payment plan.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Budget {
|
pub enum BudgetExpr {
|
||||||
/// Make a payment.
|
/// Make a payment.
|
||||||
Pay(Payment),
|
Pay(Payment),
|
||||||
|
|
||||||
|
@ -49,20 +49,20 @@ pub enum Budget {
|
||||||
And(Condition, Condition, Payment),
|
And(Condition, Condition, Payment),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Budget {
|
impl BudgetExpr {
|
||||||
/// Create the simplest budget - one that pays `tokens` to Pubkey.
|
/// Create the simplest budget - one that pays `tokens` to Pubkey.
|
||||||
pub fn new_payment(tokens: i64, to: Pubkey) -> Self {
|
pub fn new_payment(tokens: i64, to: Pubkey) -> Self {
|
||||||
Budget::Pay(Payment { tokens, to })
|
BudgetExpr::Pay(Payment { tokens, to })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a budget that pays `tokens` to `to` after being witnessed by `from`.
|
/// Create a budget that pays `tokens` to `to` after being witnessed by `from`.
|
||||||
pub fn new_authorized_payment(from: Pubkey, tokens: i64, to: Pubkey) -> Self {
|
pub fn new_authorized_payment(from: Pubkey, tokens: i64, to: Pubkey) -> Self {
|
||||||
Budget::After(Condition::Signature(from), Payment { tokens, to })
|
BudgetExpr::After(Condition::Signature(from), Payment { tokens, to })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a budget that pays tokens` to `to` after being witnessed by 2x `from`s
|
/// Create a budget that pays tokens` to `to` after being witnessed by 2x `from`s
|
||||||
pub fn new_2_2_multisig_payment(from0: Pubkey, from1: Pubkey, tokens: i64, to: Pubkey) -> Self {
|
pub fn new_2_2_multisig_payment(from0: Pubkey, from1: Pubkey, tokens: i64, to: Pubkey) -> Self {
|
||||||
Budget::And(
|
BudgetExpr::And(
|
||||||
Condition::Signature(from0),
|
Condition::Signature(from0),
|
||||||
Condition::Signature(from1),
|
Condition::Signature(from1),
|
||||||
Payment { tokens, to },
|
Payment { tokens, to },
|
||||||
|
@ -71,7 +71,7 @@ impl Budget {
|
||||||
|
|
||||||
/// Create a budget that pays `tokens` to `to` after the given DateTime.
|
/// Create a budget that pays `tokens` to `to` after the given DateTime.
|
||||||
pub fn new_future_payment(dt: DateTime<Utc>, from: Pubkey, tokens: i64, to: Pubkey) -> Self {
|
pub fn new_future_payment(dt: DateTime<Utc>, from: Pubkey, tokens: i64, to: Pubkey) -> Self {
|
||||||
Budget::After(Condition::Timestamp(dt, from), Payment { tokens, to })
|
BudgetExpr::After(Condition::Timestamp(dt, from), Payment { tokens, to })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a budget that pays `tokens` to `to` after the given DateTime
|
/// Create a budget that pays `tokens` to `to` after the given DateTime
|
||||||
|
@ -82,7 +82,7 @@ impl Budget {
|
||||||
tokens: i64,
|
tokens: i64,
|
||||||
to: Pubkey,
|
to: Pubkey,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Budget::Or(
|
BudgetExpr::Or(
|
||||||
(Condition::Timestamp(dt, from), Payment { tokens, to }),
|
(Condition::Timestamp(dt, from), Payment { tokens, to }),
|
||||||
(Condition::Signature(from), Payment { tokens, to: from }),
|
(Condition::Signature(from), Payment { tokens, to: from }),
|
||||||
)
|
)
|
||||||
|
@ -91,7 +91,7 @@ impl Budget {
|
||||||
/// Return Payment if the budget requires no additional Witnesses.
|
/// Return Payment if the budget requires no additional Witnesses.
|
||||||
pub fn final_payment(&self) -> Option<Payment> {
|
pub fn final_payment(&self) -> Option<Payment> {
|
||||||
match self {
|
match self {
|
||||||
Budget::Pay(payment) => Some(payment.clone()),
|
BudgetExpr::Pay(payment) => Some(payment.clone()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,39 +99,39 @@ impl Budget {
|
||||||
/// Return true if the budget spends exactly `spendable_tokens`.
|
/// Return true if the budget spends exactly `spendable_tokens`.
|
||||||
pub fn verify(&self, spendable_tokens: i64) -> bool {
|
pub fn verify(&self, spendable_tokens: i64) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Budget::Pay(payment) | Budget::After(_, payment) | Budget::And(_, _, payment) => {
|
BudgetExpr::Pay(payment) | BudgetExpr::After(_, payment) | BudgetExpr::And(_, _, payment) => {
|
||||||
payment.tokens == spendable_tokens
|
payment.tokens == spendable_tokens
|
||||||
}
|
}
|
||||||
Budget::Or(a, b) => a.1.tokens == spendable_tokens && b.1.tokens == spendable_tokens,
|
BudgetExpr::Or(a, b) => a.1.tokens == spendable_tokens && b.1.tokens == spendable_tokens,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a witness to the budget to see if the budget can be reduced.
|
/// Apply a witness to the budget to see if the budget can be reduced.
|
||||||
/// If so, modify the budget in-place.
|
/// If so, modify the budget in-place.
|
||||||
pub fn apply_witness(&mut self, witness: &Witness, from: &Pubkey) {
|
pub fn apply_witness(&mut self, witness: &Witness, from: &Pubkey) {
|
||||||
let new_budget = match self {
|
let new_expr = match self {
|
||||||
Budget::After(cond, payment) if cond.is_satisfied(witness, from) => {
|
BudgetExpr::After(cond, payment) if cond.is_satisfied(witness, from) => {
|
||||||
Some(Budget::Pay(payment.clone()))
|
Some(BudgetExpr::Pay(payment.clone()))
|
||||||
}
|
}
|
||||||
Budget::Or((cond, payment), _) if cond.is_satisfied(witness, from) => {
|
BudgetExpr::Or((cond, payment), _) if cond.is_satisfied(witness, from) => {
|
||||||
Some(Budget::Pay(payment.clone()))
|
Some(BudgetExpr::Pay(payment.clone()))
|
||||||
}
|
}
|
||||||
Budget::Or(_, (cond, payment)) if cond.is_satisfied(witness, from) => {
|
BudgetExpr::Or(_, (cond, payment)) if cond.is_satisfied(witness, from) => {
|
||||||
Some(Budget::Pay(payment.clone()))
|
Some(BudgetExpr::Pay(payment.clone()))
|
||||||
}
|
}
|
||||||
Budget::And(cond0, cond1, payment) => {
|
BudgetExpr::And(cond0, cond1, payment) => {
|
||||||
if cond0.is_satisfied(witness, from) {
|
if cond0.is_satisfied(witness, from) {
|
||||||
Some(Budget::After(cond1.clone(), payment.clone()))
|
Some(BudgetExpr::After(cond1.clone(), payment.clone()))
|
||||||
} else if cond1.is_satisfied(witness, from) {
|
} else if cond1.is_satisfied(witness, from) {
|
||||||
Some(Budget::After(cond0.clone(), payment.clone()))
|
Some(BudgetExpr::After(cond0.clone(), payment.clone()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(budget) = new_budget {
|
if let Some(expr) = new_expr {
|
||||||
mem::replace(self, budget);
|
mem::replace(self, expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,10 +162,10 @@ mod tests {
|
||||||
let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
|
let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
|
||||||
let from = Pubkey::default();
|
let from = Pubkey::default();
|
||||||
let to = Pubkey::default();
|
let to = Pubkey::default();
|
||||||
assert!(Budget::new_payment(42, to).verify(42));
|
assert!(BudgetExpr::new_payment(42, to).verify(42));
|
||||||
assert!(Budget::new_authorized_payment(from, 42, to).verify(42));
|
assert!(BudgetExpr::new_authorized_payment(from, 42, to).verify(42));
|
||||||
assert!(Budget::new_future_payment(dt, from, 42, to).verify(42));
|
assert!(BudgetExpr::new_future_payment(dt, from, 42, to).verify(42));
|
||||||
assert!(Budget::new_cancelable_future_payment(dt, from, 42, to).verify(42));
|
assert!(BudgetExpr::new_cancelable_future_payment(dt, from, 42, to).verify(42));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -173,9 +173,9 @@ mod tests {
|
||||||
let from = Pubkey::default();
|
let from = Pubkey::default();
|
||||||
let to = Pubkey::default();
|
let to = Pubkey::default();
|
||||||
|
|
||||||
let mut budget = Budget::new_authorized_payment(from, 42, to);
|
let mut expr = BudgetExpr::new_authorized_payment(from, 42, to);
|
||||||
budget.apply_witness(&Witness::Signature, &from);
|
expr.apply_witness(&Witness::Signature, &from);
|
||||||
assert_eq!(budget, Budget::new_payment(42, to));
|
assert_eq!(expr, BudgetExpr::new_payment(42, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -184,9 +184,9 @@ mod tests {
|
||||||
let from = Keypair::new().pubkey();
|
let from = Keypair::new().pubkey();
|
||||||
let to = Keypair::new().pubkey();
|
let to = Keypair::new().pubkey();
|
||||||
|
|
||||||
let mut budget = Budget::new_future_payment(dt, from, 42, to);
|
let mut expr = BudgetExpr::new_future_payment(dt, from, 42, to);
|
||||||
budget.apply_witness(&Witness::Timestamp(dt), &from);
|
expr.apply_witness(&Witness::Timestamp(dt), &from);
|
||||||
assert_eq!(budget, Budget::new_payment(42, to));
|
assert_eq!(expr, BudgetExpr::new_payment(42, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -197,10 +197,10 @@ mod tests {
|
||||||
let from = Keypair::new().pubkey();
|
let from = Keypair::new().pubkey();
|
||||||
let to = Keypair::new().pubkey();
|
let to = Keypair::new().pubkey();
|
||||||
|
|
||||||
let mut budget = Budget::new_future_payment(dt, from, 42, to);
|
let mut expr = BudgetExpr::new_future_payment(dt, from, 42, to);
|
||||||
let orig_budget = budget.clone();
|
let orig_expr = expr.clone();
|
||||||
budget.apply_witness(&Witness::Timestamp(dt), &to); // <-- Attack!
|
expr.apply_witness(&Witness::Timestamp(dt), &to); // <-- Attack!
|
||||||
assert_eq!(budget, orig_budget);
|
assert_eq!(expr, orig_expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -209,13 +209,13 @@ mod tests {
|
||||||
let from = Pubkey::default();
|
let from = Pubkey::default();
|
||||||
let to = Pubkey::default();
|
let to = Pubkey::default();
|
||||||
|
|
||||||
let mut budget = Budget::new_cancelable_future_payment(dt, from, 42, to);
|
let mut expr = BudgetExpr::new_cancelable_future_payment(dt, from, 42, to);
|
||||||
budget.apply_witness(&Witness::Timestamp(dt), &from);
|
expr.apply_witness(&Witness::Timestamp(dt), &from);
|
||||||
assert_eq!(budget, Budget::new_payment(42, to));
|
assert_eq!(expr, BudgetExpr::new_payment(42, to));
|
||||||
|
|
||||||
let mut budget = Budget::new_cancelable_future_payment(dt, from, 42, to);
|
let mut expr = BudgetExpr::new_cancelable_future_payment(dt, from, 42, to);
|
||||||
budget.apply_witness(&Witness::Signature, &from);
|
expr.apply_witness(&Witness::Signature, &from);
|
||||||
assert_eq!(budget, Budget::new_payment(42, from));
|
assert_eq!(expr, BudgetExpr::new_payment(42, from));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_2_2_multisig_payment() {
|
fn test_2_2_multisig_payment() {
|
||||||
|
@ -223,8 +223,8 @@ mod tests {
|
||||||
let from1 = Keypair::new().pubkey();
|
let from1 = Keypair::new().pubkey();
|
||||||
let to = Pubkey::default();
|
let to = Pubkey::default();
|
||||||
|
|
||||||
let mut budget = Budget::new_2_2_multisig_payment(from0, from1, 42, to);
|
let mut expr = BudgetExpr::new_2_2_multisig_payment(from0, from1, 42, to);
|
||||||
budget.apply_witness(&Witness::Signature, &from0);
|
expr.apply_witness(&Witness::Signature, &from0);
|
||||||
assert_eq!(budget, Budget::new_authorized_payment(from1, 42, to));
|
assert_eq!(expr, BudgetExpr::new_authorized_payment(from1, 42, to));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,19 +1,19 @@
|
||||||
use budget::Budget;
|
use budget_expr::BudgetExpr;
|
||||||
use chrono::prelude::{DateTime, Utc};
|
use chrono::prelude::{DateTime, Utc};
|
||||||
|
|
||||||
/// A smart contract.
|
/// A smart contract.
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct Contract {
|
pub struct Contract {
|
||||||
/// The number of tokens allocated to the `Budget` and any transaction fees.
|
/// The number of tokens allocated to the `BudgetExpr` and any transaction fees.
|
||||||
pub tokens: i64,
|
pub tokens: i64,
|
||||||
pub budget: Budget,
|
pub budget_expr: BudgetExpr,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An instruction to progress the smart contract.
|
/// An instruction to progress the smart contract.
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
/// Declare and instantiate `Budget`.
|
/// Declare and instantiate `BudgetExpr`.
|
||||||
NewBudget(Budget),
|
NewBudget(BudgetExpr),
|
||||||
|
|
||||||
/// Tell a payment plan acknowledge the given `DateTime` has past.
|
/// Tell a payment plan acknowledge the given `DateTime` has past.
|
||||||
ApplyTimestamp(DateTime<Utc>),
|
ApplyTimestamp(DateTime<Utc>),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! budget program
|
//! budget program
|
||||||
use bincode::{self, deserialize, serialize_into, serialized_size};
|
use bincode::{self, deserialize, serialize_into, serialized_size};
|
||||||
use budget::Budget;
|
use budget_expr::BudgetExpr;
|
||||||
use budget_instruction::Instruction;
|
use budget_instruction::Instruction;
|
||||||
use chrono::prelude::{DateTime, Utc};
|
use chrono::prelude::{DateTime, Utc};
|
||||||
use payment_plan::Witness;
|
use payment_plan::Witness;
|
||||||
|
@ -27,7 +27,7 @@ pub enum BudgetError {
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
|
||||||
pub struct BudgetState {
|
pub struct BudgetState {
|
||||||
pub initialized: bool,
|
pub initialized: bool,
|
||||||
pub pending_budget: Option<Budget>,
|
pub pending_budget: Option<BudgetExpr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const BUDGET_PROGRAM_ID: [u8; 32] = [
|
const BUDGET_PROGRAM_ID: [u8; 32] = [
|
||||||
|
@ -55,13 +55,13 @@ impl BudgetState {
|
||||||
accounts: &mut [&mut Account],
|
accounts: &mut [&mut Account],
|
||||||
) -> Result<(), BudgetError> {
|
) -> Result<(), BudgetError> {
|
||||||
let mut final_payment = None;
|
let mut final_payment = None;
|
||||||
if let Some(ref mut budget) = self.pending_budget {
|
if let Some(ref mut expr) = self.pending_budget {
|
||||||
let key = match tx.signed_key(instruction_index, 0) {
|
let key = match tx.signed_key(instruction_index, 0) {
|
||||||
None => return Err(BudgetError::UnsignedKey),
|
None => return Err(BudgetError::UnsignedKey),
|
||||||
Some(key) => key,
|
Some(key) => key,
|
||||||
};
|
};
|
||||||
budget.apply_witness(&Witness::Signature, key);
|
expr.apply_witness(&Witness::Signature, key);
|
||||||
final_payment = budget.final_payment();
|
final_payment = expr.final_payment();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(payment) = final_payment {
|
if let Some(payment) = final_payment {
|
||||||
|
@ -88,13 +88,13 @@ impl BudgetState {
|
||||||
// Check to see if any timelocked transactions can be completed.
|
// Check to see if any timelocked transactions can be completed.
|
||||||
let mut final_payment = None;
|
let mut final_payment = None;
|
||||||
|
|
||||||
if let Some(ref mut budget) = self.pending_budget {
|
if let Some(ref mut expr) = self.pending_budget {
|
||||||
let key = match tx.signed_key(instruction_index, 0) {
|
let key = match tx.signed_key(instruction_index, 0) {
|
||||||
None => return Err(BudgetError::UnsignedKey),
|
None => return Err(BudgetError::UnsignedKey),
|
||||||
Some(key) => key,
|
Some(key) => key,
|
||||||
};
|
};
|
||||||
budget.apply_witness(&Witness::Timestamp(dt), key);
|
expr.apply_witness(&Witness::Timestamp(dt), key);
|
||||||
final_payment = budget.final_payment();
|
final_payment = expr.final_payment();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(payment) = final_payment {
|
if let Some(payment) = final_payment {
|
||||||
|
@ -120,9 +120,9 @@ impl BudgetState {
|
||||||
return Err(BudgetError::SourceIsPendingContract);
|
return Err(BudgetError::SourceIsPendingContract);
|
||||||
}
|
}
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::NewBudget(budget) => {
|
Instruction::NewBudget(expr) => {
|
||||||
let budget = budget.clone();
|
let expr = expr.clone();
|
||||||
if let Some(payment) = budget.final_payment() {
|
if let Some(payment) = expr.final_payment() {
|
||||||
accounts[1].tokens += payment.tokens;
|
accounts[1].tokens += payment.tokens;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -132,7 +132,7 @@ impl BudgetState {
|
||||||
Err(BudgetError::ContractAlreadyExists)
|
Err(BudgetError::ContractAlreadyExists)
|
||||||
} else {
|
} else {
|
||||||
let mut state = BudgetState::default();
|
let mut state = BudgetState::default();
|
||||||
state.pending_budget = Some(budget);
|
state.pending_budget = Some(expr);
|
||||||
accounts[1].tokens += accounts[0].tokens;
|
accounts[1].tokens += accounts[0].tokens;
|
||||||
accounts[0].tokens = 0;
|
accounts[0].tokens = 0;
|
||||||
state.initialized = true;
|
state.initialized = true;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! The `budget_transaction` module provides functionality for creating Budget transactions.
|
//! The `budget_transaction` module provides functionality for creating Budget transactions.
|
||||||
|
|
||||||
use bincode::{deserialize, serialize};
|
use bincode::{deserialize, serialize};
|
||||||
use budget::{Budget, Condition};
|
use budget_expr::{BudgetExpr, Condition};
|
||||||
use budget_instruction::Instruction;
|
use budget_instruction::Instruction;
|
||||||
use budget_program::BudgetState;
|
use budget_program::BudgetState;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
|
@ -84,7 +84,7 @@ impl BudgetTransaction for Transaction {
|
||||||
tokens: tokens - fee,
|
tokens: tokens - fee,
|
||||||
to,
|
to,
|
||||||
};
|
};
|
||||||
let budget_instruction = Instruction::NewBudget(Budget::Pay(payment));
|
let budget_instruction = Instruction::NewBudget(BudgetExpr::Pay(payment));
|
||||||
let pay_userdata = serialize(&budget_instruction).unwrap();
|
let pay_userdata = serialize(&budget_instruction).unwrap();
|
||||||
|
|
||||||
let program_ids = vec![SystemProgram::id(), BudgetState::id()];
|
let program_ids = vec![SystemProgram::id(), BudgetState::id()];
|
||||||
|
@ -160,15 +160,15 @@ impl BudgetTransaction for Transaction {
|
||||||
tokens: i64,
|
tokens: i64,
|
||||||
last_id: Hash,
|
last_id: Hash,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let budget = if let Some(from) = cancelable {
|
let expr = if let Some(from) = cancelable {
|
||||||
Budget::Or(
|
BudgetExpr::Or(
|
||||||
(Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to }),
|
(Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to }),
|
||||||
(Condition::Signature(from), Payment { tokens, to: from }),
|
(Condition::Signature(from), Payment { tokens, to: from }),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Budget::After(Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to })
|
BudgetExpr::After(Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to })
|
||||||
};
|
};
|
||||||
let instruction = Instruction::NewBudget(budget);
|
let instruction = Instruction::NewBudget(expr);
|
||||||
let userdata = serialize(&instruction).expect("serialize instruction");
|
let userdata = serialize(&instruction).expect("serialize instruction");
|
||||||
Self::new(
|
Self::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
|
@ -189,15 +189,15 @@ impl BudgetTransaction for Transaction {
|
||||||
tokens: i64,
|
tokens: i64,
|
||||||
last_id: Hash,
|
last_id: Hash,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let budget = if let Some(from) = cancelable {
|
let expr = if let Some(from) = cancelable {
|
||||||
Budget::Or(
|
BudgetExpr::Or(
|
||||||
(Condition::Signature(witness), Payment { tokens, to }),
|
(Condition::Signature(witness), Payment { tokens, to }),
|
||||||
(Condition::Signature(from), Payment { tokens, to: from }),
|
(Condition::Signature(from), Payment { tokens, to: from }),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Budget::After(Condition::Signature(witness), Payment { tokens, to })
|
BudgetExpr::After(Condition::Signature(witness), Payment { tokens, to })
|
||||||
};
|
};
|
||||||
let instruction = Instruction::NewBudget(budget);
|
let instruction = Instruction::NewBudget(expr);
|
||||||
let userdata = serialize(&instruction).expect("serialize instruction");
|
let userdata = serialize(&instruction).expect("serialize instruction");
|
||||||
Self::new(
|
Self::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
|
@ -220,8 +220,8 @@ impl BudgetTransaction for Transaction {
|
||||||
/// Verify only the payment plan.
|
/// Verify only the payment plan.
|
||||||
fn verify_plan(&self) -> bool {
|
fn verify_plan(&self) -> bool {
|
||||||
if let Some(SystemProgram::Move { tokens }) = self.system_instruction(0) {
|
if let Some(SystemProgram::Move { tokens }) = self.system_instruction(0) {
|
||||||
if let Some(Instruction::NewBudget(budget)) = self.instruction(1) {
|
if let Some(Instruction::NewBudget(expr)) = self.instruction(1) {
|
||||||
if !(self.fee >= 0 && self.fee <= tokens && budget.verify(tokens - self.fee)) {
|
if !(self.fee >= 0 && self.fee <= tokens && expr.verify(tokens - self.fee)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,11 +267,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialize_claim() {
|
fn test_serialize_claim() {
|
||||||
let budget = Budget::Pay(Payment {
|
let expr = BudgetExpr::Pay(Payment {
|
||||||
tokens: 0,
|
tokens: 0,
|
||||||
to: Default::default(),
|
to: Default::default(),
|
||||||
});
|
});
|
||||||
let instruction = Instruction::NewBudget(budget);
|
let instruction = Instruction::NewBudget(expr);
|
||||||
let userdata = serialize(&instruction).unwrap();
|
let userdata = serialize(&instruction).unwrap();
|
||||||
let instructions = vec![transaction::Instruction {
|
let instructions = vec![transaction::Instruction {
|
||||||
program_ids_index: 0,
|
program_ids_index: 0,
|
||||||
|
@ -301,8 +301,8 @@ mod tests {
|
||||||
if let SystemProgram::Move { ref mut tokens } = system_instruction {
|
if let SystemProgram::Move { ref mut tokens } = system_instruction {
|
||||||
*tokens = 1_000_000; // <-- attack, part 1!
|
*tokens = 1_000_000; // <-- attack, part 1!
|
||||||
let mut instruction = tx.instruction(1).unwrap();
|
let mut instruction = tx.instruction(1).unwrap();
|
||||||
if let Instruction::NewBudget(ref mut budget) = instruction {
|
if let Instruction::NewBudget(ref mut expr) = instruction {
|
||||||
if let Budget::Pay(ref mut payment) = budget {
|
if let BudgetExpr::Pay(ref mut payment) = expr {
|
||||||
payment.tokens = *tokens; // <-- attack, part 2!
|
payment.tokens = *tokens; // <-- attack, part 2!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -322,8 +322,8 @@ mod tests {
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
let mut tx = Transaction::budget_new(&keypair0, pubkey1, 42, zero);
|
let mut tx = Transaction::budget_new(&keypair0, pubkey1, 42, zero);
|
||||||
let mut instruction = tx.instruction(1);
|
let mut instruction = tx.instruction(1);
|
||||||
if let Some(Instruction::NewBudget(ref mut budget)) = instruction {
|
if let Some(Instruction::NewBudget(ref mut expr)) = instruction {
|
||||||
if let Budget::Pay(ref mut payment) = budget {
|
if let BudgetExpr::Pay(ref mut payment) = expr {
|
||||||
payment.to = thief_keypair.pubkey(); // <-- attack!
|
payment.to = thief_keypair.pubkey(); // <-- attack!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,8 +339,8 @@ mod tests {
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
let mut tx = Transaction::budget_new(&keypair0, keypair1.pubkey(), 1, zero);
|
let mut tx = Transaction::budget_new(&keypair0, keypair1.pubkey(), 1, zero);
|
||||||
let mut instruction = tx.instruction(1).unwrap();
|
let mut instruction = tx.instruction(1).unwrap();
|
||||||
if let Instruction::NewBudget(ref mut budget) = instruction {
|
if let Instruction::NewBudget(ref mut expr) = instruction {
|
||||||
if let Budget::Pay(ref mut payment) = budget {
|
if let BudgetExpr::Pay(ref mut payment) = expr {
|
||||||
payment.tokens = 2; // <-- attack!
|
payment.tokens = 2; // <-- attack!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,8 +349,8 @@ mod tests {
|
||||||
|
|
||||||
// Also, ensure all branchs of the plan spend all tokens
|
// Also, ensure all branchs of the plan spend all tokens
|
||||||
let mut instruction = tx.instruction(1).unwrap();
|
let mut instruction = tx.instruction(1).unwrap();
|
||||||
if let Instruction::NewBudget(ref mut budget) = instruction {
|
if let Instruction::NewBudget(ref mut expr) = instruction {
|
||||||
if let Budget::Pay(ref mut payment) = budget {
|
if let BudgetExpr::Pay(ref mut payment) = expr {
|
||||||
payment.tokens = 0; // <-- whoops!
|
payment.tokens = 0; // <-- whoops!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub mod banking_stage;
|
||||||
pub mod blob_fetch_stage;
|
pub mod blob_fetch_stage;
|
||||||
pub mod bpf_loader;
|
pub mod bpf_loader;
|
||||||
pub mod broadcast_stage;
|
pub mod broadcast_stage;
|
||||||
pub mod budget;
|
pub mod budget_expr;
|
||||||
pub mod budget_instruction;
|
pub mod budget_instruction;
|
||||||
pub mod budget_transaction;
|
pub mod budget_transaction;
|
||||||
#[cfg(feature = "chacha")]
|
#[cfg(feature = "chacha")]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//! The `plan` module provides a domain-specific language for payment plans. Users create Budget objects that
|
//! The `plan` module provides a domain-specific language for payment plans. Users create BudgetExpr objects that
|
||||||
//! are given to an interpreter. The interpreter listens for `Witness` transactions,
|
//! are given to an interpreter. The interpreter listens for `Witness` transactions,
|
||||||
//! which it uses to reduce the payment plan. When the plan is reduced to a
|
//! which it uses to reduce the payment plan. When the plan is reduced to a
|
||||||
//! `Payment`, the payment is executed.
|
//! `Payment`, the payment is executed.
|
||||||
|
|
Loading…
Reference in New Issue