Boot lua loader
Good fun, but unnecessary and I haven't been updating the rlua dependency. If someone wants this, it can be developed outside the solana repo.
This commit is contained in:
parent
cedee73548
commit
731e5e1291
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
[package]
|
||||
name = "solana-lualoader"
|
||||
version = "0.12.0"
|
||||
description = "Solana Lua Loader"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
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"]
|
||||
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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<u8> {
|
||||
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!
|
||||
}
|
||||
}
|
|
@ -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"))]
|
||||
|
|
Loading…
Reference in New Issue