Revive payments via Budget
This commit is contained in:
@@ -29,10 +29,13 @@ pub enum BudgetInstruction {
|
||||
|
||||
impl BudgetInstruction {
|
||||
pub fn new_initialize_account(contract: Pubkey, expr: BudgetExpr) -> BuilderInstruction {
|
||||
BuilderInstruction::new(
|
||||
id(),
|
||||
&BudgetInstruction::InitializeAccount(expr),
|
||||
vec![(contract, false)],
|
||||
)
|
||||
let mut keys = vec![];
|
||||
if let BudgetExpr::Pay(payment) = &expr {
|
||||
keys.push((payment.to, false));
|
||||
} else {
|
||||
panic!("unsupported Budget instruction");
|
||||
}
|
||||
keys.push((contract, false));
|
||||
BuilderInstruction::new(id(), &BudgetInstruction::InitializeAccount(expr), keys)
|
||||
}
|
||||
}
|
||||
|
74
programs/budget_api/src/budget_state.rs
Normal file
74
programs/budget_api/src/budget_state.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
//! budget state
|
||||
use crate::budget_expr::BudgetExpr;
|
||||
use bincode::{self, deserialize, serialize_into};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub enum BudgetError {
|
||||
InsufficientFunds,
|
||||
ContractAlreadyExists,
|
||||
ContractNotPending,
|
||||
SourceIsPendingContract,
|
||||
UninitializedContract,
|
||||
NegativeTokens,
|
||||
DestinationMissing,
|
||||
FailedWitness,
|
||||
UserdataTooSmall,
|
||||
UserdataDeserializeFailure,
|
||||
UnsignedKey,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
|
||||
pub struct BudgetState {
|
||||
pub initialized: bool,
|
||||
pub pending_budget: Option<BudgetExpr>,
|
||||
}
|
||||
|
||||
impl BudgetState {
|
||||
pub fn new(budget_expr: BudgetExpr) -> Self {
|
||||
Self {
|
||||
initialized: true,
|
||||
pending_budget: Some(budget_expr),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_pending(&self) -> bool {
|
||||
self.pending_budget.is_some()
|
||||
}
|
||||
|
||||
pub fn serialize(&self, output: &mut [u8]) -> Result<(), BudgetError> {
|
||||
serialize_into(output, self).map_err(|err| match *err {
|
||||
_ => BudgetError::UserdataTooSmall,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn deserialize(input: &[u8]) -> bincode::Result<Self> {
|
||||
deserialize(input)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::id;
|
||||
use solana_sdk::account::Account;
|
||||
|
||||
#[test]
|
||||
fn test_serializer() {
|
||||
let mut a = Account::new(0, 512, id());
|
||||
let b = BudgetState::default();
|
||||
b.serialize(&mut a.userdata).unwrap();
|
||||
let c = BudgetState::deserialize(&a.userdata).unwrap();
|
||||
assert_eq!(b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serializer_userdata_too_small() {
|
||||
let mut a = Account::new(0, 1, id());
|
||||
let b = BudgetState::default();
|
||||
assert_eq!(
|
||||
b.serialize(&mut a.userdata),
|
||||
Err(BudgetError::UserdataTooSmall)
|
||||
);
|
||||
}
|
||||
}
|
@@ -2,8 +2,9 @@
|
||||
|
||||
use crate::budget_expr::{BudgetExpr, Condition};
|
||||
use crate::budget_instruction::BudgetInstruction;
|
||||
use crate::budget_state::BudgetState;
|
||||
use crate::id;
|
||||
use bincode::deserialize;
|
||||
use bincode::{deserialize, serialized_size};
|
||||
use chrono::prelude::*;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
@@ -26,8 +27,15 @@ impl BudgetTransaction {
|
||||
let contract = Keypair::new().pubkey();
|
||||
let from = from_keypair.pubkey();
|
||||
let payment = BudgetExpr::new_payment(tokens - fee, to);
|
||||
let space = serialized_size(&BudgetState::new(payment.clone())).unwrap();
|
||||
TransactionBuilder::new(fee)
|
||||
.push(SystemInstruction::new_move(from, contract, tokens))
|
||||
.push(SystemInstruction::new_program_account(
|
||||
from,
|
||||
contract,
|
||||
tokens,
|
||||
space,
|
||||
id(),
|
||||
))
|
||||
.push(BudgetInstruction::new_initialize_account(contract, payment))
|
||||
.sign(&[from_keypair], recent_blockhash)
|
||||
}
|
||||
@@ -163,7 +171,9 @@ impl BudgetTransaction {
|
||||
|
||||
/// Verify only the payment plan.
|
||||
pub fn verify_plan(tx: &Transaction) -> bool {
|
||||
if let Some(SystemInstruction::Move { tokens }) = Self::system_instruction(tx, 0) {
|
||||
if let Some(SystemInstruction::CreateAccount { tokens, .. }) =
|
||||
Self::system_instruction(tx, 0)
|
||||
{
|
||||
if let Some(BudgetInstruction::InitializeAccount(expr)) =
|
||||
BudgetTransaction::instruction(&tx, 1)
|
||||
{
|
||||
@@ -226,7 +236,7 @@ mod tests {
|
||||
let pubkey = keypair.pubkey();
|
||||
let mut tx = BudgetTransaction::new(&keypair, pubkey, 42, zero);
|
||||
let mut system_instruction = BudgetTransaction::system_instruction(&tx, 0).unwrap();
|
||||
if let SystemInstruction::Move { ref mut tokens } = system_instruction {
|
||||
if let SystemInstruction::CreateAccount { ref mut tokens, .. } = system_instruction {
|
||||
*tokens = 1_000_000; // <-- attack, part 1!
|
||||
let mut instruction = BudgetTransaction::instruction(&tx, 1).unwrap();
|
||||
if let BudgetInstruction::InitializeAccount(ref mut expr) = instruction {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
pub mod budget_expr;
|
||||
pub mod budget_instruction;
|
||||
pub mod budget_state;
|
||||
pub mod budget_transaction;
|
||||
pub mod payment_plan;
|
||||
|
||||
|
Reference in New Issue
Block a user