2019-03-16 13:30:10 -07:00
|
|
|
use crate::budget_expr::{BudgetExpr, Condition};
|
|
|
|
use crate::budget_state::BudgetState;
|
2019-03-02 13:23:22 -08:00
|
|
|
use crate::id;
|
2019-03-16 13:30:10 -07:00
|
|
|
use bincode::serialized_size;
|
2018-09-17 13:36:31 -07:00
|
|
|
use chrono::prelude::{DateTime, Utc};
|
2019-03-02 13:23:22 -08:00
|
|
|
use serde_derive::{Deserialize, Serialize};
|
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2019-03-17 08:55:42 -07:00
|
|
|
use solana_sdk::script::Script;
|
2019-03-16 16:37:18 -07:00
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
2019-03-16 13:30:10 -07:00
|
|
|
use solana_sdk::system_instruction::SystemInstruction;
|
2019-03-17 08:55:42 -07:00
|
|
|
use solana_sdk::transaction::Instruction;
|
2018-09-17 13:36:31 -07:00
|
|
|
|
2018-10-25 16:58:40 -07:00
|
|
|
/// A smart contract.
|
2018-09-17 13:36:31 -07:00
|
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
2018-10-25 16:58:40 -07:00
|
|
|
pub struct Contract {
|
2019-03-05 17:27:25 -08:00
|
|
|
/// The number of lamports allocated to the `BudgetExpr` and any transaction fees.
|
|
|
|
pub lamports: u64,
|
2018-11-02 19:13:33 -07:00
|
|
|
pub budget_expr: BudgetExpr,
|
2018-09-17 13:36:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// An instruction to progress the smart contract.
|
|
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
2019-03-03 13:17:51 -08:00
|
|
|
pub enum BudgetInstruction {
|
2018-11-02 19:13:33 -07:00
|
|
|
/// Declare and instantiate `BudgetExpr`.
|
2019-03-03 13:17:51 -08:00
|
|
|
InitializeAccount(BudgetExpr),
|
2018-09-17 13:36:31 -07:00
|
|
|
|
|
|
|
/// Tell a payment plan acknowledge the given `DateTime` has past.
|
|
|
|
ApplyTimestamp(DateTime<Utc>),
|
|
|
|
|
2019-03-03 13:17:51 -08:00
|
|
|
/// Tell the budget that the `InitializeAccount` with `Signature` has been
|
2018-09-17 13:36:31 -07:00
|
|
|
/// signed by the containing transaction's `Pubkey`.
|
2018-09-18 18:45:44 -07:00
|
|
|
ApplySignature,
|
2018-09-17 13:36:31 -07:00
|
|
|
}
|
2019-02-28 03:48:44 -08:00
|
|
|
|
2019-03-03 13:17:51 -08:00
|
|
|
impl BudgetInstruction {
|
2019-03-15 08:47:25 -07:00
|
|
|
pub fn new_initialize_account(contract: &Pubkey, expr: BudgetExpr) -> Instruction {
|
2019-03-03 14:43:51 -08:00
|
|
|
let mut keys = vec![];
|
|
|
|
if let BudgetExpr::Pay(payment) = &expr {
|
|
|
|
keys.push((payment.to, false));
|
|
|
|
}
|
2019-03-09 19:28:43 -08:00
|
|
|
keys.push((*contract, false));
|
2019-03-15 08:47:25 -07:00
|
|
|
Instruction::new(id(), &BudgetInstruction::InitializeAccount(expr), keys)
|
2019-02-28 03:48:44 -08:00
|
|
|
}
|
2019-03-06 21:12:14 -08:00
|
|
|
|
2019-03-16 13:30:10 -07:00
|
|
|
pub fn new_initialize_account_script(
|
|
|
|
from: &Pubkey,
|
|
|
|
contract: &Pubkey,
|
|
|
|
lamports: u64,
|
|
|
|
expr: BudgetExpr,
|
|
|
|
) -> Script {
|
|
|
|
let space = serialized_size(&BudgetState::new(expr.clone())).unwrap();
|
2019-03-17 08:55:42 -07:00
|
|
|
Script::new(vec![
|
2019-03-16 13:30:10 -07:00
|
|
|
SystemInstruction::new_program_account(&from, contract, lamports, space, &id()),
|
|
|
|
Self::new_initialize_account(contract, expr),
|
2019-03-17 08:55:42 -07:00
|
|
|
])
|
2019-03-16 13:30:10 -07:00
|
|
|
}
|
|
|
|
|
2019-03-16 16:37:18 -07:00
|
|
|
/// Create a new payment script.
|
|
|
|
pub fn new_payment_script(from: &Pubkey, to: &Pubkey, lamports: u64) -> Script {
|
|
|
|
let contract = Keypair::new().pubkey();
|
|
|
|
let expr = BudgetExpr::new_payment(lamports, to);
|
|
|
|
Self::new_initialize_account_script(from, &contract, lamports, expr)
|
|
|
|
}
|
|
|
|
|
2019-03-16 13:30:10 -07:00
|
|
|
/// Create a postdated payment script.
|
|
|
|
pub fn new_on_date_script(
|
|
|
|
from: &Pubkey,
|
|
|
|
to: &Pubkey,
|
|
|
|
contract: &Pubkey,
|
|
|
|
dt: DateTime<Utc>,
|
|
|
|
dt_pubkey: &Pubkey,
|
|
|
|
cancelable: Option<Pubkey>,
|
|
|
|
lamports: u64,
|
|
|
|
) -> Script {
|
|
|
|
let expr = if let Some(from) = cancelable {
|
|
|
|
BudgetExpr::Or(
|
|
|
|
(
|
|
|
|
Condition::Timestamp(dt, *dt_pubkey),
|
|
|
|
Box::new(BudgetExpr::new_payment(lamports, to)),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
Condition::Signature(from),
|
|
|
|
Box::new(BudgetExpr::new_payment(lamports, &from)),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
BudgetExpr::After(
|
|
|
|
Condition::Timestamp(dt, *dt_pubkey),
|
|
|
|
Box::new(BudgetExpr::new_payment(lamports, to)),
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
Self::new_initialize_account_script(from, contract, lamports, expr)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a multisig payment script.
|
|
|
|
pub fn new_when_signed_script(
|
|
|
|
from: &Pubkey,
|
|
|
|
to: &Pubkey,
|
|
|
|
contract: &Pubkey,
|
|
|
|
witness: &Pubkey,
|
|
|
|
cancelable: Option<Pubkey>,
|
|
|
|
lamports: u64,
|
|
|
|
) -> Script {
|
|
|
|
let expr = if let Some(from) = cancelable {
|
|
|
|
BudgetExpr::Or(
|
|
|
|
(
|
|
|
|
Condition::Signature(*witness),
|
|
|
|
Box::new(BudgetExpr::new_payment(lamports, to)),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
Condition::Signature(from),
|
|
|
|
Box::new(BudgetExpr::new_payment(lamports, &from)),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
BudgetExpr::After(
|
|
|
|
Condition::Signature(*witness),
|
|
|
|
Box::new(BudgetExpr::new_payment(lamports, to)),
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
Self::new_initialize_account_script(from, contract, lamports, expr)
|
|
|
|
}
|
|
|
|
|
2019-03-06 21:12:14 -08:00
|
|
|
pub fn new_apply_timestamp(
|
2019-03-09 19:28:43 -08:00
|
|
|
from: &Pubkey,
|
|
|
|
contract: &Pubkey,
|
|
|
|
to: &Pubkey,
|
2019-03-06 21:12:14 -08:00
|
|
|
dt: DateTime<Utc>,
|
2019-03-15 08:47:25 -07:00
|
|
|
) -> Instruction {
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut keys = vec![(*from, true), (*contract, false)];
|
2019-03-07 12:34:13 -08:00
|
|
|
if from != to {
|
2019-03-09 19:28:43 -08:00
|
|
|
keys.push((*to, false));
|
2019-03-07 12:34:13 -08:00
|
|
|
}
|
2019-03-15 08:47:25 -07:00
|
|
|
Instruction::new(id(), &BudgetInstruction::ApplyTimestamp(dt), keys)
|
2019-03-06 21:12:14 -08:00
|
|
|
}
|
|
|
|
|
2019-03-15 08:47:25 -07:00
|
|
|
pub fn new_apply_signature(from: &Pubkey, contract: &Pubkey, to: &Pubkey) -> Instruction {
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut keys = vec![(*from, true), (*contract, false)];
|
2019-03-07 12:34:13 -08:00
|
|
|
if from != to {
|
2019-03-09 19:28:43 -08:00
|
|
|
keys.push((*to, false));
|
2019-03-07 12:34:13 -08:00
|
|
|
}
|
2019-03-15 08:47:25 -07:00
|
|
|
Instruction::new(id(), &BudgetInstruction::ApplySignature, keys)
|
2019-03-06 21:12:14 -08:00
|
|
|
}
|
2019-02-28 03:48:44 -08:00
|
|
|
}
|