diff --git a/Cargo.lock b/Cargo.lock index 951fc7f4f4..b140ab11c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1743,17 +1743,6 @@ dependencies = [ "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rlua" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rocksdb" version = "0.11.0" @@ -2141,19 +2130,6 @@ dependencies = [ "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "solana-lualoader" -version = "0.12.0" -dependencies = [ - "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rlua 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)", - "solana-logger 0.12.0", - "solana-sdk 0.12.0", -] - [[package]] name = "solana-metrics" version = "0.12.0" @@ -2208,7 +2184,6 @@ dependencies = [ "solana 0.12.0", "solana-bpfloader 0.12.0", "solana-logger 0.12.0", - "solana-lualoader 0.12.0", "solana-native-loader 0.12.0", "solana-sdk 0.12.0", ] @@ -3028,7 +3003,6 @@ dependencies = [ "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum reqwest 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)" = "09d6e187a58d923ee132fcda141c94e716bcfe301c2ea2bef5c81536e0085376" "checksum ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4db68a2e35f3497146b7e4563df7d4773a2433230c5e4b448328e31740458a" -"checksum rlua 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)" = "187f5174337682c1ae2d957b107f4c84e204ce2e084c76532194d3849db09a28" "checksum rocksdb 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1651697fefd273bfb4fd69466cc2a9d20de557a0213b97233b22b5e95924b5e" "checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" diff --git a/Cargo.toml b/Cargo.toml index 7805e3e623..d2c741c748 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,7 +106,6 @@ members = [ "programs/native/bpf_loader", "programs/native/budget", "programs/native/erc20", - "programs/native/lua_loader", "programs/native/native_loader", "programs/native/noop", "programs/native/storage", diff --git a/ci/publish-crate.sh b/ci/publish-crate.sh index cedf2785c1..c25efc6405 100755 --- a/ci/publish-crate.sh +++ b/ci/publish-crate.sh @@ -17,7 +17,7 @@ CRATES=( keygen metrics drone - programs/native/{budget,bpf_loader,lua_loader,native_loader,noop,system,vote} + programs/native/{budget,bpf_loader,native_loader,noop,system,vote} . fullnode-config fullnode diff --git a/programs/Cargo.toml b/programs/Cargo.toml index 16c12cf641..76fd2b68cc 100644 --- a/programs/Cargo.toml +++ b/programs/Cargo.toml @@ -21,6 +21,5 @@ erasure = ["solana/erasure"] solana = { path = "..", version = "0.12.0" } solana-bpfloader = { path = "native/bpf_loader", version = "0.12.0" } solana-logger = { path = "../logger", version = "0.12.0" } -solana-lualoader = { path = "native/lua_loader", version = "0.12.0" } solana-native-loader = { path = "native/native_loader", version = "0.12.0" } solana-sdk = { path = "../sdk", version = "0.12.0" } diff --git a/programs/native/lua_loader/Cargo.toml b/programs/native/lua_loader/Cargo.toml deleted file mode 100644 index a1592da6bc..0000000000 --- a/programs/native/lua_loader/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "solana-lualoader" -version = "0.12.0" -description = "Solana Lua Loader" -authors = ["Solana Maintainers "] -repository = "https://github.com/solana-labs/solana" -license = "Apache-2.0" -homepage = "https://solana.com/" -edition = "2018" - -[dependencies] -bincode = "1.0.0" -log = "0.4.2" -rlua = "0.15.2" -serde = "1.0.87" -serde_derive = "1.0.87" -solana-logger = { path = "../../../logger", version = "0.12.0" } -solana-sdk = { path = "../../../sdk", version = "0.12.0" } - -[dev-dependencies] -bincode = "1.0.0" - -[lib] -name = "solana_lua_loader" -crate-type = ["cdylib"] - diff --git a/programs/native/lua_loader/multisig.lua b/programs/native/lua_loader/multisig.lua deleted file mode 100644 index 132f36fc22..0000000000 --- a/programs/native/lua_loader/multisig.lua +++ /dev/null @@ -1,50 +0,0 @@ --- M-N Multisig. Pass in a table "{m=M, n=N, tokens=T}" where M is the number --- of signatures required, and N is a list of the pubkeys identifying --- those signatures. Once M of len(N) signatures are collected, tokens T --- are subtracted from account 1 and given to account 4. Note that unlike --- Rust, Lua is one-based and that account 1 is the first account. - -function find(t, x) - for i, v in pairs(t) do - if v == x then - return i - end - end -end - -function deserialize(bytes) - return load("return" .. bytes)() -end - -local from_account, - serialize_account, - state_account, - to_account = table.unpack(accounts) - -local serialize = load(serialize_account.userdata)().serialize - -if #state_account.userdata == 0 then - local cfg = deserialize(data) - state_account.userdata = serialize(cfg, nil, "s") - return -end - -local cfg = deserialize(state_account.userdata) -local key = deserialize(data) - -local i = find(cfg.n, key) -if i == nil then - return -end - -table.remove(cfg.n, i) -cfg.m = cfg.m - 1 -state_account.userdata = serialize(cfg, nil, "s") - -if cfg.m == 0 then - from_account.tokens = from_account.tokens - cfg.tokens - to_account.tokens = to_account.tokens + cfg.tokens - - -- End of game. - state_account.tokens = 0 -end diff --git a/programs/native/lua_loader/serialize.lua b/programs/native/lua_loader/serialize.lua deleted file mode 100644 index a2a086b22f..0000000000 --- a/programs/native/lua_loader/serialize.lua +++ /dev/null @@ -1,174 +0,0 @@ ----------------------------------------------------------------- --- serialize.lua --- --- Exports: --- --- orderedPairs : deterministically ordered version of pairs() --- --- serialize : convert Lua value to string in Lua syntax --- ----------------------------------------------------------------- - - --- orderedPairs: iterate over table elements in deterministic order. First, --- array elements are returned, then remaining elements sorted by the key's --- type and value. - --- compare any two Lua values, establishing a complete ordering -local function ltAny(a,b) - local ta, tb = type(a), type(b) - if ta ~= tb then - return ta < tb - end - if ta == "string" or ta == "number" then - return a < b - end - return tostring(a) < tostring(b) -end - -local inext = ipairs{} - -local function orderedPairs(t) - local keys = {} - local keyIndex = 1 - local counting = true - - local function _next(seen, s) - local v - - if counting then - -- return next array index - s, v = inext(t, s) - if s ~= nil then - seen[s] = true - return s,v - end - counting = false - - -- construct sorted unseen keys - for k,v in pairs(t) do - if not seen[k] then - table.insert(keys, k) - end - end - table.sort(keys, ltAny) - end - - -- return next unseen table element - s = keys[keyIndex] - if s ~= nil then - keyIndex = keyIndex + 1 - v = t[s] - end - return s, v - end - - return _next, {}, 0 -end - - --- avoid 'nan', 'inf', and '-inf' -local numtostring = { - [tostring(-1/0)] = "-1/0", - [tostring(1/0)] = "1/0", - [tostring(0/0)] = "0/0" -} - -setmetatable(numtostring, { __index = function (t, k) return k end }) - --- serialize: Serialize a Lua data structure --- --- x = value to serialize --- out = function to be called repeatedly with strings, or --- table into which strings should be inserted, or --- nil => return a string --- iter = function to iterate over table elements, or --- "s" to sort elements by key, or --- nil for default (fastest) --- --- Notes: --- * Does not support self-referential data structures. --- * Does not optimize for repeated sub-expressions. --- * Does not preserve topology; only values. --- * Does not handle types other than nil, number, boolean, string, table --- -local function serialize(x, out, iter) - local visited = {} - local iter = iter=="s" and orderedPairs or iter or pairs - assert(type(iter) == "function") - - local function _serialize(x) - if type(x) == "string" then - - out(string.format("%q", x)) - - elseif type(x) == "number" then - - out(numtostring[tostring(x)]) - - elseif type(x) == "boolean" or - type(x) == "nil" then - - out(tostring(x)) - - elseif type(x) == "table" then - - if visited[x] then - error("serialize: recursive structure") - end - visited[x] = true - local first, nextIndex = true, 1 - - out "{" - - for k,v in iter(x) do - if first then - first = false - else - out "," - end - if k == nextIndex then - nextIndex = nextIndex + 1 - else - if type(k) == "string" and k:match("^[%a_][%w_]*$") then - out(k.."=") - else - out "[" - _serialize(k) - out "]=" - end - end - _serialize(v) - end - - out "}" - visited[x] = false - else - error("serialize: unsupported type") - end - end - - local result - if not out then - result = {} - out = result - end - - if type(out) == "table" then - local t = out - function out(s) - table.insert(t,s) - end - end - - _serialize(x) - - if result then - return table.concat(result) - end -end - -return { - orderedPairs = orderedPairs, - serialize = serialize -} diff --git a/programs/native/lua_loader/src/lib.rs b/programs/native/lua_loader/src/lib.rs deleted file mode 100644 index 2833b4bc73..0000000000 --- a/programs/native/lua_loader/src/lib.rs +++ /dev/null @@ -1,306 +0,0 @@ -use log::*; -use rlua::{Lua, Table}; -use solana_sdk::account::KeyedAccount; -use solana_sdk::loader_instruction::LoaderInstruction; -use solana_sdk::native_program::ProgramError; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::solana_entrypoint; -use std::str; - -/// Make KeyAccount values available to Lua. -fn set_accounts(lua: &Lua, name: &str, keyed_accounts: &[KeyedAccount]) -> rlua::Result<()> { - let accounts = lua.create_table()?; - for (i, keyed_account) in keyed_accounts.iter().enumerate() { - let account = lua.create_table()?; - account.set( - if keyed_account.signer_key().is_some() { - "signer_key" - } else { - "unsigned_key" - }, - keyed_account.unsigned_key().to_string(), - )?; - account.set("tokens", keyed_account.account.tokens)?; - let data_str = lua.create_string(&keyed_account.account.userdata)?; - account.set("userdata", data_str)?; - 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 [KeyedAccount]) -> rlua::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")?; - let data_str: rlua::String = account.get("userdata")?; - keyed_account.account.userdata = data_str.as_bytes().to_vec(); - } - Ok(()) -} - -fn run_lua(keyed_accounts: &mut [KeyedAccount], code: &str, data: &[u8]) -> rlua::Result<()> { - let lua = Lua::new(); - let globals = lua.globals(); - let data_str = lua.create_string(data)?; - globals.set("data", data_str)?; - - set_accounts(&lua, "accounts", keyed_accounts)?; - lua.exec::<_, ()>(code, None)?; - update_accounts(&lua, "accounts", keyed_accounts) -} - -solana_entrypoint!(entrypoint); -fn entrypoint( - _program_id: &Pubkey, - keyed_accounts: &mut [KeyedAccount], - tx_data: &[u8], - _tick_height: u64, -) -> Result<(), ProgramError> { - solana_logger::setup(); - - if keyed_accounts[0].account.executable { - let (codes, params) = keyed_accounts.split_at_mut(1); - let code = &codes[0].account.userdata; - let code = str::from_utf8(code).unwrap(); - match run_lua(params, &code, tx_data) { - Ok(()) => { - trace!("Lua success"); - } - Err(e) => { - warn!("Lua Error: {:#?}", e); - return Err(ProgramError::GenericError); - } - } - } else if let Ok(instruction) = bincode::deserialize(tx_data) { - if keyed_accounts[0].signer_key().is_none() { - warn!("key[0] did not sign the transaction"); - return Err(ProgramError::GenericError); - } - match instruction { - LoaderInstruction::Write { offset, bytes } => { - let offset = offset as usize; - let len = bytes.len(); - trace!("LuaLoader::Write offset {} length {:?}", offset, len); - if keyed_accounts[0].account.userdata.len() < offset + len { - warn!( - "Write overflow {} < {}", - keyed_accounts[0].account.userdata.len(), - offset + len - ); - return Err(ProgramError::GenericError); - } - keyed_accounts[0].account.userdata[offset..offset + len].copy_from_slice(&bytes); - } - - LoaderInstruction::Finalize => { - keyed_accounts[0].account.executable = true; - trace!( - "LuaLoader::Finalize prog: {:?}", - keyed_accounts[0].signer_key().unwrap() - ); - } - } - } else { - warn!("Invalid program transaction: {:?}", tx_data); - return Err(ProgramError::GenericError); - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use bincode::serialize; - use solana_sdk::account::{create_keyed_accounts, Account}; - use solana_sdk::pubkey::Pubkey; - use std::fs::File; - use std::io::prelude::*; - use std::path::PathBuf; - - #[test] - fn test_update_accounts() { - 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).unwrap(); - keyed_accounts[0].account.tokens = 42; - keyed_accounts[0].account.userdata = vec![]; - update_accounts(&lua, "xs", &mut keyed_accounts).unwrap(); - - // Ensure update_accounts() overwrites the local value 42. - assert_eq!(keyed_accounts[0].account.tokens, 0); - } - - #[test] - fn test_credit_with_lua() { - let code = r#"accounts[1].tokens = accounts[1].tokens + 1"#; - let mut accounts = [(Pubkey::default(), Account::default())]; - run_lua(&mut create_keyed_accounts(&mut accounts), code, &[]).unwrap(); - assert_eq!(accounts[0].1.tokens, 1); - } - - #[test] - fn test_error_with_lua() { - let code = r#"accounts[1].tokens += 1"#; - let mut accounts = [(Pubkey::default(), Account::default())]; - assert!(run_lua(&mut create_keyed_accounts(&mut accounts), code, &[]).is_err()); - } - - #[test] - fn test_move_funds_with_lua_via_process() { - let userdata = r#" - local tokens, _ = string.unpack("I", data) - accounts[1].tokens = accounts[1].tokens - tokens - accounts[2].tokens = accounts[2].tokens + tokens - "# - .as_bytes() - .to_vec(); - - let alice_pubkey = Pubkey::default(); - let bob_pubkey = Pubkey::default(); - let owner = Pubkey::default(); - - let mut accounts = [ - ( - Pubkey::default(), - Account { - tokens: 1, - userdata, - owner, - executable: true, - loader: Pubkey::default(), - }, - ), - (alice_pubkey, Account::new(100, 0, owner)), - (bob_pubkey, Account::new(1, 0, owner)), - ]; - let data = serialize(&10u64).unwrap(); - process(&owner, &mut create_keyed_accounts(&mut accounts), &data, 0).unwrap(); - assert_eq!(accounts[1].1.tokens, 90); - assert_eq!(accounts[2].1.tokens, 11); - - process(&owner, &mut create_keyed_accounts(&mut accounts), &data, 0).unwrap(); - assert_eq!(accounts[1].1.tokens, 80); - assert_eq!(accounts[2].1.tokens, 21); - } - - fn read_test_file(name: &str) -> Vec { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push(name); - let mut file = File::open(path).unwrap(); - let mut contents = vec![]; - file.read_to_end(&mut contents).unwrap(); - contents - } - - #[test] - fn test_load_lua_library() { - let userdata = r#" - local serialize = load(accounts[2].userdata)().serialize - accounts[3].userdata = serialize({a=1, b=2, c=3}, nil, "s") - "# - .as_bytes() - .to_vec(); - let owner = Pubkey::default(); - let program_account = Account { - tokens: 1, - userdata, - owner, - executable: true, - loader: Pubkey::default(), - }; - let alice_account = Account::new(100, 0, owner); - let serialize_account = Account { - tokens: 100, - userdata: read_test_file("serialize.lua"), - owner, - executable: false, - loader: Pubkey::default(), - }; - let mut accounts = [ - (Pubkey::default(), program_account), - (Pubkey::default(), alice_account), - (Pubkey::default(), serialize_account), - (Pubkey::default(), Account::new(1, 0, owner)), - ]; - let mut keyed_accounts = create_keyed_accounts(&mut accounts); - process(&owner, &mut keyed_accounts, &[], 0).unwrap(); - // Verify deterministic ordering of a serialized Lua table. - assert_eq!( - str::from_utf8(&keyed_accounts[3].account.userdata).unwrap(), - "{a=1,b=2,c=3}" - ); - } - - #[test] - fn test_lua_multisig() { - let owner = Pubkey::default(); - - let alice_pubkey = Pubkey::new(&[0; 32]); - let serialize_pubkey = Pubkey::new(&[1; 32]); - let state_pubkey = Pubkey::new(&[2; 32]); - let bob_pubkey = Pubkey::new(&[3; 32]); - let carol_pubkey = Pubkey::new(&[4; 32]); - let dan_pubkey = Pubkey::new(&[5; 32]); - let erin_pubkey = Pubkey::new(&[6; 32]); - - let program_account = Account { - tokens: 1, - userdata: read_test_file("multisig.lua"), - owner, - executable: true, - loader: Pubkey::default(), - }; - - let alice_account = Account { - tokens: 100, - userdata: Vec::new(), - owner, - executable: true, - loader: Pubkey::default(), - }; - - let serialize_account = Account { - tokens: 100, - userdata: read_test_file("serialize.lua"), - owner, - executable: true, - loader: Pubkey::default(), - }; - - let mut accounts = [ - (Pubkey::default(), program_account), // Account holding the program - (alice_pubkey, alice_account), // The payer - (serialize_pubkey, serialize_account), // Where the serialize library is stored. - (state_pubkey, Account::new(1, 0, owner)), // Where program state is stored. - (bob_pubkey, Account::new(1, 0, owner)), // The payee once M signatures are collected. - ]; - let mut keyed_accounts = create_keyed_accounts(&mut accounts); - - let data = format!( - r#"{{m=2, n={{"{}","{}","{}"}}, tokens=100}}"#, - carol_pubkey, dan_pubkey, erin_pubkey - ) - .as_bytes() - .to_vec(); - - process(&owner, &mut keyed_accounts, &data, 0).unwrap(); - assert_eq!(keyed_accounts[4].account.tokens, 1); - - let data = format!(r#""{}""#, carol_pubkey).into_bytes(); - process(&owner, &mut keyed_accounts, &data, 0).unwrap(); - assert_eq!(keyed_accounts[4].account.tokens, 1); - - let data = format!(r#""{}""#, dan_pubkey).into_bytes(); - process(&owner, &mut keyed_accounts, &data, 0).unwrap(); - assert_eq!(keyed_accounts[4].account.tokens, 101); // Pay day! - - let data = format!(r#""{}""#, erin_pubkey).into_bytes(); - process(&owner, &mut keyed_accounts, &data, 0).unwrap(); - assert_eq!(keyed_accounts[4].account.tokens, 101); // No change! - } -} diff --git a/programs/tests/programs.rs b/programs/tests/programs.rs index 098e099d1b..25565fe793 100644 --- a/programs/tests/programs.rs +++ b/programs/tests/programs.rs @@ -60,60 +60,6 @@ fn test_program_native_noop() { bank.process_transaction(&tx).unwrap(); } -#[test] -fn test_program_lua_move_funds() { - solana_logger::setup(); - - let (genesis_block, mint_keypair) = GenesisBlock::new(50); - let bank = Bank::new(&genesis_block); - let loader_id = load_program( - &bank, - &mint_keypair, - native_loader::id(), - "solana_lua_loader".as_bytes().to_vec(), - ); - - let program = r#" - print("Lua Script!") - local tokens, _ = string.unpack("I", data) - accounts[1].tokens = accounts[1].tokens - tokens - accounts[2].tokens = accounts[2].tokens + tokens - "# - .as_bytes() - .to_vec(); - let program_id = load_program(&bank, &mint_keypair, loader_id, program); - let from = Keypair::new(); - let to = Keypair::new().pubkey(); - - // Call user program with two accounts - let tx = SystemTransaction::new_program_account( - &mint_keypair, - from.pubkey(), - bank.last_id(), - 10, - 0, - program_id, - 0, - ); - bank.process_transaction(&tx).unwrap(); - - let tx = SystemTransaction::new_program_account( - &mint_keypair, - to, - bank.last_id(), - 1, - 0, - program_id, - 0, - ); - bank.process_transaction(&tx).unwrap(); - - let tx = Transaction::new(&from, &[to], program_id, &10, bank.last_id(), 0); - bank.process_transaction(&tx).unwrap(); - assert_eq!(bank.get_balance(&from.pubkey()), 0); - assert_eq!(bank.get_balance(&to), 11); -} - #[cfg(feature = "bpf_c")] use solana_sdk::bpf_loader; #[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]