2018-10-09 06:29:09 -06:00
|
|
|
extern crate rlua;
|
|
|
|
extern crate solana_program_interface;
|
|
|
|
|
|
|
|
use rlua::{Lua, Result, Table};
|
|
|
|
use solana_program_interface::account::KeyedAccount;
|
2018-10-09 09:45:09 -06:00
|
|
|
use std::str;
|
2018-10-09 06:29:09 -06:00
|
|
|
|
|
|
|
/// Make KeyAccount values available to Lua.
|
|
|
|
fn set_accounts(lua: &Lua, name: &str, keyed_accounts: &[KeyedAccount]) -> Result<()> {
|
|
|
|
let accounts = lua.create_table()?;
|
|
|
|
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
|
|
|
|
let account = lua.create_table()?;
|
|
|
|
account.set("tokens", keyed_account.account.tokens)?;
|
2018-10-09 09:45:09 -06:00
|
|
|
let data_str = lua.create_string(&keyed_account.account.userdata)?;
|
|
|
|
account.set("userdata", data_str)?;
|
2018-10-09 06:29:09 -06:00
|
|
|
accounts.set(i + 1, account)?;
|
|
|
|
}
|
|
|
|
let globals = lua.globals();
|
|
|
|
globals.set(name, accounts)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Commit the new KeyedAccount values.
|
|
|
|
fn update_accounts(lua: &Lua, name: &str, keyed_accounts: &mut Vec<KeyedAccount>) -> Result<()> {
|
|
|
|
let globals = lua.globals();
|
|
|
|
let accounts: Table = globals.get(name)?;
|
|
|
|
for (i, keyed_account) in keyed_accounts.into_iter().enumerate() {
|
|
|
|
let account: Table = accounts.get(i + 1)?;
|
|
|
|
keyed_account.account.tokens = account.get("tokens")?;
|
2018-10-09 09:45:09 -06:00
|
|
|
let data_str: rlua::String = account.get("userdata")?;
|
|
|
|
keyed_account.account.userdata = data_str.as_bytes().to_vec();
|
2018-10-09 06:29:09 -06:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2018-10-09 08:02:00 -06:00
|
|
|
fn run_lua(keyed_accounts: &mut Vec<KeyedAccount>, code: &str, data: &[u8]) -> Result<()> {
|
2018-10-09 06:29:09 -06:00
|
|
|
let lua = Lua::new();
|
2018-10-09 08:02:00 -06:00
|
|
|
let globals = lua.globals();
|
|
|
|
let data_str = lua.create_string(data)?;
|
|
|
|
globals.set("data", data_str)?;
|
|
|
|
|
2018-10-09 06:29:09 -06:00
|
|
|
set_accounts(&lua, "accounts", keyed_accounts)?;
|
|
|
|
lua.exec::<_, ()>(code, None)?;
|
|
|
|
update_accounts(&lua, "accounts", keyed_accounts)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "C" fn process(keyed_accounts: &mut Vec<KeyedAccount>, data: &[u8]) {
|
2018-10-09 09:45:09 -06:00
|
|
|
let code_data = keyed_accounts[0].account.userdata.clone();
|
|
|
|
let code = str::from_utf8(&code_data).unwrap();
|
2018-10-09 08:02:00 -06:00
|
|
|
run_lua(keyed_accounts, &code, data).unwrap();
|
2018-10-09 06:29:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2018-10-09 09:45:09 -06:00
|
|
|
extern crate bincode;
|
|
|
|
|
|
|
|
use self::bincode::serialize;
|
2018-10-09 06:29:09 -06:00
|
|
|
use super::*;
|
|
|
|
use solana_program_interface::account::{create_keyed_accounts, Account};
|
|
|
|
use solana_program_interface::pubkey::Pubkey;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_update_accounts() -> Result<()> {
|
|
|
|
let mut accounts = [(Pubkey::default(), Account::default())];
|
|
|
|
let mut keyed_accounts = create_keyed_accounts(&mut accounts);
|
|
|
|
let lua = Lua::new();
|
|
|
|
set_accounts(&lua, "xs", &keyed_accounts)?;
|
|
|
|
keyed_accounts[0].account.tokens = 42;
|
2018-10-09 09:45:09 -06:00
|
|
|
keyed_accounts[0].account.userdata = vec![];
|
2018-10-09 06:29:09 -06:00
|
|
|
update_accounts(&lua, "xs", &mut keyed_accounts)?;
|
|
|
|
|
|
|
|
// Ensure update_accounts() overwrites the local value 42.
|
|
|
|
assert_eq!(keyed_accounts[0].account.tokens, 0);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_credit_with_lua() -> Result<()> {
|
|
|
|
let code = r#"accounts[1].tokens = accounts[1].tokens + 1"#;
|
|
|
|
let mut accounts = [(Pubkey::default(), Account::default())];
|
2018-10-09 08:02:00 -06:00
|
|
|
run_lua(&mut create_keyed_accounts(&mut accounts), code, &[])?;
|
2018-10-09 06:29:09 -06:00
|
|
|
assert_eq!(accounts[0].1.tokens, 1);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_error_with_lua() {
|
|
|
|
let code = r#"accounts[1].tokens += 1"#;
|
|
|
|
let mut accounts = [(Pubkey::default(), Account::default())];
|
2018-10-09 08:02:00 -06:00
|
|
|
assert!(run_lua(&mut create_keyed_accounts(&mut accounts), code, &[]).is_err());
|
2018-10-09 06:29:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_move_funds_with_lua_via_process() {
|
2018-10-09 09:45:09 -06:00
|
|
|
let userdata = r#"
|
2018-10-09 08:02:00 -06:00
|
|
|
local tokens, _ = string.unpack("I", data)
|
2018-10-09 06:29:09 -06:00
|
|
|
accounts[1].tokens = accounts[1].tokens - tokens
|
|
|
|
accounts[2].tokens = accounts[2].tokens + tokens
|
2018-10-09 09:45:09 -06:00
|
|
|
"#.as_bytes()
|
|
|
|
.to_vec();
|
2018-10-09 06:29:09 -06:00
|
|
|
|
|
|
|
let alice_pubkey = Pubkey::default();
|
|
|
|
let bob_pubkey = Pubkey::default();
|
|
|
|
let program_id = Pubkey::default();
|
|
|
|
|
|
|
|
let mut accounts = [
|
2018-10-09 08:02:00 -06:00
|
|
|
(
|
|
|
|
alice_pubkey,
|
|
|
|
Account {
|
|
|
|
tokens: 100,
|
|
|
|
userdata,
|
|
|
|
program_id,
|
|
|
|
},
|
|
|
|
),
|
2018-10-09 06:29:09 -06:00
|
|
|
(bob_pubkey, Account::new(1, 0, program_id)),
|
|
|
|
];
|
2018-10-09 09:45:09 -06:00
|
|
|
let data = serialize(&10u64).unwrap();
|
2018-10-09 06:29:09 -06:00
|
|
|
process(&mut create_keyed_accounts(&mut accounts), &data);
|
2018-10-09 08:02:00 -06:00
|
|
|
assert_eq!(accounts[0].1.tokens, 90);
|
|
|
|
assert_eq!(accounts[1].1.tokens, 11);
|
2018-10-09 06:29:09 -06:00
|
|
|
|
2018-10-09 08:02:00 -06:00
|
|
|
process(&mut create_keyed_accounts(&mut accounts), &data);
|
|
|
|
assert_eq!(accounts[0].1.tokens, 80);
|
|
|
|
assert_eq!(accounts[1].1.tokens, 21);
|
2018-10-09 06:29:09 -06:00
|
|
|
}
|
2018-10-09 09:45:09 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_move_funds_with_lua_via_process_and_terminate() {
|
|
|
|
let userdata = r#"
|
|
|
|
local tokens, _ = string.unpack("I", data)
|
|
|
|
accounts[1].tokens = accounts[1].tokens - tokens
|
|
|
|
accounts[2].tokens = accounts[2].tokens + tokens
|
|
|
|
accounts[1].userdata = ""
|
|
|
|
"#.as_bytes()
|
|
|
|
.to_vec();
|
|
|
|
|
|
|
|
let alice_pubkey = Pubkey::default();
|
|
|
|
let bob_pubkey = Pubkey::default();
|
|
|
|
let program_id = Pubkey::default();
|
|
|
|
|
|
|
|
let mut accounts = [
|
|
|
|
(
|
|
|
|
alice_pubkey,
|
|
|
|
Account {
|
|
|
|
tokens: 100,
|
|
|
|
userdata,
|
|
|
|
program_id,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
(bob_pubkey, Account::new(1, 0, program_id)),
|
|
|
|
];
|
|
|
|
let data = serialize(&10).unwrap();
|
|
|
|
process(&mut create_keyed_accounts(&mut accounts), &data);
|
|
|
|
assert_eq!(accounts[0].1.tokens, 90);
|
|
|
|
assert_eq!(accounts[1].1.tokens, 11);
|
|
|
|
|
|
|
|
// Verify the program modified itself to a no-op.
|
|
|
|
process(&mut create_keyed_accounts(&mut accounts), &data);
|
|
|
|
assert_eq!(accounts[0].1.tokens, 90);
|
|
|
|
assert_eq!(accounts[1].1.tokens, 11);
|
|
|
|
}
|
2018-10-09 06:29:09 -06:00
|
|
|
}
|