verify signature is on the from account
This commit is contained in:
@ -36,64 +36,80 @@ pub fn id() -> Pubkey {
|
|||||||
pub fn get_balance(account: &Account) -> u64 {
|
pub fn get_balance(account: &Account) -> u64 {
|
||||||
account.tokens
|
account.tokens
|
||||||
}
|
}
|
||||||
pub fn process_instruction(
|
|
||||||
tx: &Transaction,
|
fn process_instruction(tx: &Transaction, pix: usize, accounts: &mut [&mut Account]) -> Result<()> {
|
||||||
pix: usize,
|
|
||||||
accounts: &mut [&mut Account],
|
|
||||||
) -> Result<()> {
|
|
||||||
if let Ok(syscall) = deserialize(tx.userdata(pix)) {
|
if let Ok(syscall) = deserialize(tx.userdata(pix)) {
|
||||||
trace!("process_instruction: {:?}", syscall);
|
trace!("process_instruction: {:?}", syscall);
|
||||||
|
let from = 0;
|
||||||
|
|
||||||
|
// all system instructions require that accounts_keys[0] be a signer
|
||||||
|
if tx.signed_key(pix, from).is_none() {
|
||||||
|
Err(Error::InvalidArgument)?;
|
||||||
|
}
|
||||||
|
|
||||||
match syscall {
|
match syscall {
|
||||||
SystemInstruction::CreateAccount {
|
SystemInstruction::CreateAccount {
|
||||||
tokens,
|
tokens,
|
||||||
space,
|
space,
|
||||||
program_id,
|
program_id,
|
||||||
} => {
|
} => {
|
||||||
if !check_id(&accounts[0].owner) {
|
let to = 1;
|
||||||
info!("Invalid account[0] owner");
|
|
||||||
|
if !check_id(&accounts[from].owner) {
|
||||||
|
info!("CreateAccount: invalid account[from] owner");
|
||||||
Err(Error::InvalidArgument)?;
|
Err(Error::InvalidArgument)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if space > 0 && (!accounts[1].userdata.is_empty() || !check_id(&accounts[1].owner))
|
if space > 0
|
||||||
|
&& (!accounts[to].userdata.is_empty() || !check_id(&accounts[to].owner))
|
||||||
{
|
{
|
||||||
info!("Invalid account[1]");
|
info!(
|
||||||
|
"CreateAccount: invalid argument space: {} accounts.userdata.len(): {}",
|
||||||
|
space,
|
||||||
|
accounts[to].userdata.len(),
|
||||||
|
);
|
||||||
Err(Error::InvalidArgument)?;
|
Err(Error::InvalidArgument)?;
|
||||||
}
|
}
|
||||||
if tokens > accounts[0].tokens {
|
if tokens > accounts[from].tokens {
|
||||||
info!(
|
info!(
|
||||||
"Insufficient tokens in account[0] ({} > {})",
|
"CreateAccount: insufficient tokens ({}, need {})",
|
||||||
tokens, accounts[0].tokens
|
accounts[from].tokens, tokens
|
||||||
);
|
);
|
||||||
Err(Error::ResultWithNegativeTokens)?;
|
Err(Error::ResultWithNegativeTokens)?;
|
||||||
}
|
}
|
||||||
accounts[0].tokens -= tokens;
|
accounts[from].tokens -= tokens;
|
||||||
accounts[1].tokens += tokens;
|
accounts[to].tokens += tokens;
|
||||||
accounts[1].owner = program_id;
|
accounts[to].owner = program_id;
|
||||||
accounts[1].userdata = vec![0; space as usize];
|
accounts[to].userdata = vec![0; space as usize];
|
||||||
accounts[1].executable = false;
|
accounts[to].executable = false;
|
||||||
accounts[1].loader = Pubkey::default();
|
accounts[to].loader = Pubkey::default();
|
||||||
}
|
}
|
||||||
SystemInstruction::Assign { program_id } => {
|
SystemInstruction::Assign { program_id } => {
|
||||||
if !check_id(&accounts[0].owner) {
|
if !check_id(&accounts[from].owner) {
|
||||||
Err(Error::AssignOfUnownedAccount)?;
|
Err(Error::AssignOfUnownedAccount)?;
|
||||||
}
|
}
|
||||||
accounts[0].owner = program_id;
|
accounts[from].owner = program_id;
|
||||||
}
|
}
|
||||||
SystemInstruction::Move { tokens } => {
|
SystemInstruction::Move { tokens } => {
|
||||||
|
let to = 1;
|
||||||
|
|
||||||
//bank should be verifying correctness
|
//bank should be verifying correctness
|
||||||
if tokens > accounts[0].tokens {
|
if tokens > accounts[from].tokens {
|
||||||
info!("Insufficient tokens in account[0]");
|
info!(
|
||||||
|
"Move: insufficient tokens ({}, need {})",
|
||||||
|
accounts[from].tokens, tokens
|
||||||
|
);
|
||||||
Err(Error::ResultWithNegativeTokens)?;
|
Err(Error::ResultWithNegativeTokens)?;
|
||||||
}
|
}
|
||||||
accounts[0].tokens -= tokens;
|
accounts[from].tokens -= tokens;
|
||||||
accounts[1].tokens += tokens;
|
accounts[to].tokens += tokens;
|
||||||
}
|
}
|
||||||
SystemInstruction::Spawn => {
|
SystemInstruction::Spawn => {
|
||||||
if !accounts[0].executable || accounts[0].loader != Pubkey::default() {
|
if !accounts[from].executable || accounts[from].loader != Pubkey::default() {
|
||||||
Err(Error::AccountNotFinalized)?;
|
Err(Error::AccountNotFinalized)?;
|
||||||
}
|
}
|
||||||
accounts[0].loader = accounts[0].owner;
|
accounts[from].loader = accounts[from].owner;
|
||||||
accounts[0].owner = tx.account_keys[0];
|
accounts[from].owner = tx.account_keys[from];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -124,9 +140,30 @@ mod test {
|
|||||||
use system_transaction::SystemTransaction;
|
use system_transaction::SystemTransaction;
|
||||||
use transaction::Transaction;
|
use transaction::Transaction;
|
||||||
|
|
||||||
|
/// Execute a function with a subset of accounts as writable references.
|
||||||
|
/// Since the subset can point to the same references, in any order there is no way
|
||||||
|
/// for the borrow checker to track them with regards to the original set.
|
||||||
|
fn with_subset<F, A>(accounts: &mut [Account], ixes: &[u8], func: F) -> A
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut [&mut Account]) -> A,
|
||||||
|
{
|
||||||
|
let mut subset: Vec<&mut Account> = ixes
|
||||||
|
.iter()
|
||||||
|
.map(|ix| {
|
||||||
|
let ptr = &mut accounts[*ix as usize] as *mut Account;
|
||||||
|
// lifetime of this unsafe is only within the scope of the closure
|
||||||
|
// there is no way to reorder them without breaking borrow checker rules
|
||||||
|
unsafe { &mut *ptr }
|
||||||
|
}).collect();
|
||||||
|
func(&mut subset)
|
||||||
|
}
|
||||||
fn process_transaction(tx: &Transaction, accounts: &mut [Account]) -> Result<()> {
|
fn process_transaction(tx: &Transaction, accounts: &mut [Account]) -> Result<()> {
|
||||||
let mut refs: Vec<&mut Account> = accounts.iter_mut().collect();
|
for (instruction_index, instruction) in tx.instructions.iter().enumerate() {
|
||||||
super::process_instruction(&tx, 0, &mut refs[..])
|
with_subset(accounts, &instruction.accounts, |mut program_accounts| {
|
||||||
|
super::process_instruction(tx, instruction_index, &mut program_accounts)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -251,7 +288,21 @@ mod test {
|
|||||||
assert_eq!(accounts[0].tokens, 0);
|
assert_eq!(accounts[0].tokens, 0);
|
||||||
assert_eq!(accounts[1].tokens, 1);
|
assert_eq!(accounts[1].tokens, 1);
|
||||||
}
|
}
|
||||||
/// Detect binary changes in the serialized program userdata, which could have a downstream
|
|
||||||
|
#[test]
|
||||||
|
fn test_move_many() {
|
||||||
|
let from = Keypair::new();
|
||||||
|
let tos = [(Keypair::new().pubkey(), 2), (Keypair::new().pubkey(), 1)];
|
||||||
|
let mut accounts = vec![Account::default(), Account::default(), Account::default()];
|
||||||
|
accounts[0].tokens = 3;
|
||||||
|
let tx = Transaction::system_move_many(&from, &tos, Hash::default(), 0);
|
||||||
|
process_transaction(&tx, &mut accounts).unwrap();
|
||||||
|
assert_eq!(accounts[0].tokens, 0);
|
||||||
|
assert_eq!(accounts[1].tokens, 2);
|
||||||
|
assert_eq!(accounts[2].tokens, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect binary changes in the serialized program userdata, which could have a downstream
|
||||||
/// affect on SDKs and DApps
|
/// affect on SDKs and DApps
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sdk_serialize() {
|
fn test_sdk_serialize() {
|
||||||
|
Reference in New Issue
Block a user