Remove move_loader and librapay (#11184)

* Remove move_loader and librapay

* Remove Embedding Move from implemented proposals

* Remove Move variant from CI

* Remove move_loader ID
This commit is contained in:
Greg Fitzgerald
2020-07-23 15:08:59 -06:00
committed by GitHub
parent 6de8da05e3
commit 8b1b392be9
30 changed files with 57 additions and 11632 deletions

1420
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -66,6 +66,4 @@ members = [
exclude = [
"programs/bpf",
"programs/move_loader",
"programs/librapay",
]

View File

@ -19,14 +19,12 @@ solana-core = { path = "../core", version = "1.3.0" }
solana-genesis = { path = "../genesis", version = "1.3.0" }
solana-client = { path = "../client", version = "1.3.0" }
solana-faucet = { path = "../faucet", version = "1.3.0" }
solana-librapay = { path = "../programs/librapay", version = "1.3.0", optional = true }
solana-logger = { path = "../logger", version = "1.3.0" }
solana-metrics = { path = "../metrics", version = "1.3.0" }
solana-measure = { path = "../measure", version = "1.3.0" }
solana-net-utils = { path = "../net-utils", version = "1.3.0" }
solana-runtime = { path = "../runtime", version = "1.3.0" }
solana-sdk = { path = "../sdk", version = "1.3.0" }
solana-move-loader-program = { path = "../programs/move_loader", version = "1.3.0", optional = true }
solana-version = { path = "../version", version = "1.3.0" }
[dev-dependencies]
@ -34,8 +32,5 @@ serial_test = "0.4.0"
serial_test_derive = "0.4.0"
solana-local-cluster = { path = "../local-cluster", version = "1.3.0" }
[features]
move = ["solana-librapay", "solana-move-loader-program"]
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -4,8 +4,6 @@ use rayon::prelude::*;
use solana_client::perf_utils::{sample_txs, SampleStats};
use solana_core::gen_keys::GenKeys;
use solana_faucet::faucet::request_airdrop_transaction;
#[cfg(feature = "move")]
use solana_librapay::{create_genesis, upload_mint_script, upload_payment_script};
use solana_measure::measure::Measure;
use solana_metrics::{self, datapoint_info};
use solana_sdk::{
@ -37,9 +35,6 @@ use std::{
const MAX_TX_QUEUE_AGE: u64 =
MAX_PROCESSING_AGE as u64 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND;
#[cfg(feature = "move")]
use solana_librapay::librapay_transaction;
pub const MAX_SPENDS_PER_TX: u64 = 4;
#[derive(Debug)]
@ -51,8 +46,6 @@ pub type Result<T> = std::result::Result<T, BenchTpsError>;
pub type SharedTransactions = Arc<RwLock<VecDeque<Vec<(Transaction, u64)>>>>;
type LibraKeys = (Keypair, Pubkey, Pubkey, Vec<Keypair>);
fn get_recent_blockhash<T: Client>(client: &T) -> (Hash, FeeCalculator) {
loop {
match client.get_recent_blockhash_with_commitment(CommitmentConfig::recent()) {
@ -122,7 +115,6 @@ fn generate_chunked_transfers(
threads: usize,
duration: Duration,
sustained: bool,
libra_args: Option<LibraKeys>,
) {
// generate and send transactions for the specified duration
let start = Instant::now();
@ -137,7 +129,6 @@ fn generate_chunked_transfers(
&dest_keypair_chunks[chunk_index],
threads,
reclaim_lamports_back_to_source_account,
&libra_args,
);
// In sustained mode, overlap the transfers with generation. This has higher average
@ -205,12 +196,7 @@ where
.collect()
}
pub fn do_bench_tps<T>(
client: Arc<T>,
config: Config,
gen_keypairs: Vec<Keypair>,
libra_args: Option<LibraKeys>,
) -> u64
pub fn do_bench_tps<T>(client: Arc<T>, config: Config, gen_keypairs: Vec<Keypair>) -> u64
where
T: 'static + Client + Send + Sync,
{
@ -294,7 +280,6 @@ where
threads,
duration,
sustained,
libra_args,
);
// Stop the sampling threads so it will collect the stats
@ -340,52 +325,6 @@ fn metrics_submit_lamport_balance(lamport_balance: u64) {
);
}
#[cfg(feature = "move")]
fn generate_move_txs(
source: &[&Keypair],
dest: &VecDeque<&Keypair>,
reclaim: bool,
move_keypairs: &[Keypair],
libra_pay_program_id: &Pubkey,
libra_mint_id: &Pubkey,
blockhash: &Hash,
) -> Vec<(Transaction, u64)> {
let count = move_keypairs.len() / 2;
let source_move = &move_keypairs[..count];
let dest_move = &move_keypairs[count..];
let pairs: Vec<_> = if !reclaim {
source_move
.iter()
.zip(dest_move.iter())
.zip(source.iter())
.collect()
} else {
dest_move
.iter()
.zip(source_move.iter())
.zip(dest.iter())
.collect()
};
pairs
.par_iter()
.map(|((from, to), payer)| {
(
librapay_transaction::transfer(
libra_pay_program_id,
libra_mint_id,
&payer,
&from,
&to.pubkey(),
1,
*blockhash,
),
timestamp(),
)
})
.collect()
}
fn generate_system_txs(
source: &[&Keypair],
dest: &VecDeque<&Keypair>,
@ -416,7 +355,6 @@ fn generate_txs(
dest: &VecDeque<&Keypair>,
threads: usize,
reclaim: bool,
libra_args: &Option<LibraKeys>,
) {
let blockhash = *blockhash.read().unwrap();
let tx_count = source.len();
@ -426,33 +364,7 @@ fn generate_txs(
);
let signing_start = Instant::now();
let transactions = if let Some((
_libra_genesis_keypair,
_libra_pay_program_id,
_libra_mint_program_id,
_libra_keys,
)) = libra_args
{
#[cfg(not(feature = "move"))]
{
return;
}
#[cfg(feature = "move")]
{
generate_move_txs(
source,
dest,
reclaim,
&_libra_keys,
_libra_pay_program_id,
&_libra_genesis_keypair.pubkey(),
&blockhash,
)
}
} else {
generate_system_txs(source, dest, reclaim, &blockhash)
};
let transactions = generate_system_txs(source, dest, reclaim, &blockhash);
let duration = signing_start.elapsed();
let ns = duration.as_secs() * 1_000_000_000 + u64::from(duration.subsec_nanos());
@ -954,181 +866,13 @@ pub fn generate_keypairs(seed_keypair: &Keypair, count: u64) -> (Vec<Keypair>, u
(rnd.gen_n_keypairs(total_keys), extra)
}
#[cfg(feature = "move")]
fn fund_move_keys<T: Client>(
client: &T,
funding_key: &Keypair,
keypairs: &[Keypair],
total: u64,
libra_pay_program_id: &Pubkey,
libra_mint_program_id: &Pubkey,
libra_genesis_key: &Keypair,
) {
let (mut blockhash, _fee_calculator) = get_recent_blockhash(client);
info!("creating the libra funding account..");
let libra_funding_key = Keypair::new();
let tx = librapay_transaction::create_account(funding_key, &libra_funding_key, 1, blockhash);
client
.send_and_confirm_message(&[funding_key, &libra_funding_key], tx.message)
.unwrap();
info!("minting to funding keypair");
let tx = librapay_transaction::mint_tokens(
&libra_mint_program_id,
funding_key,
libra_genesis_key,
&libra_funding_key.pubkey(),
total,
blockhash,
);
client
.send_and_confirm_message(&[funding_key, libra_genesis_key], tx.message)
.unwrap();
info!("creating {} move accounts...", keypairs.len());
let total_len = keypairs.len();
let create_len = 5;
let mut funding_time = Measure::start("funding_time");
for (i, keys) in keypairs.chunks(create_len).enumerate() {
if client
.get_balance_with_commitment(&keys[0].pubkey(), CommitmentConfig::recent())
.unwrap_or(0)
> 0
{
// already created these accounts.
break;
}
let keypairs: Vec<_> = keys.iter().map(|k| k).collect();
let tx = librapay_transaction::create_accounts(funding_key, &keypairs, 1, blockhash);
let ser_size = bincode::serialized_size(&tx).unwrap();
let mut keys = vec![funding_key];
keys.extend(&keypairs);
client.send_and_confirm_message(&keys, tx.message).unwrap();
if i % 10 == 0 {
info!(
"created {} accounts of {} (size {})",
i,
total_len / create_len,
ser_size,
);
}
}
const NUM_FUNDING_KEYS: usize = 10;
let funding_keys: Vec<_> = (0..NUM_FUNDING_KEYS).map(|_| Keypair::new()).collect();
let pubkey_amounts: Vec<_> = funding_keys
.iter()
.map(|key| (key.pubkey(), total / NUM_FUNDING_KEYS as u64))
.collect();
let instructions = system_instruction::transfer_many(&funding_key.pubkey(), &pubkey_amounts);
let message = Message::new(&instructions, Some(&funding_key.pubkey()));
let tx = Transaction::new(&[funding_key], message, blockhash);
client
.send_and_confirm_message(&[funding_key], tx.message)
.unwrap();
let mut balance = 0;
for _ in 0..20 {
if let Ok(balance_) = client
.get_balance_with_commitment(&funding_keys[0].pubkey(), CommitmentConfig::recent())
{
if balance_ > 0 {
balance = balance_;
break;
}
}
sleep(Duration::from_millis(100));
}
assert!(balance > 0);
info!(
"funded multiple funding accounts with {:?} lanports",
balance
);
let libra_funding_keys: Vec<_> = (0..NUM_FUNDING_KEYS).map(|_| Keypair::new()).collect();
for (i, key) in libra_funding_keys.iter().enumerate() {
let tx = librapay_transaction::create_account(&funding_keys[i], &key, 1, blockhash);
client
.send_and_confirm_message(&[&funding_keys[i], &key], tx.message)
.unwrap();
let tx = librapay_transaction::transfer(
libra_pay_program_id,
&libra_genesis_key.pubkey(),
&funding_keys[i],
&libra_funding_key,
&key.pubkey(),
total / NUM_FUNDING_KEYS as u64,
blockhash,
);
client
.send_and_confirm_message(&[&funding_keys[i], &libra_funding_key], tx.message)
.unwrap();
info!("funded libra funding key {}", i);
}
let keypair_count = keypairs.len();
let amount = total / (keypair_count as u64);
for (i, keys) in keypairs[..keypair_count]
.chunks(NUM_FUNDING_KEYS)
.enumerate()
{
for (j, key) in keys.iter().enumerate() {
let tx = librapay_transaction::transfer(
libra_pay_program_id,
&libra_genesis_key.pubkey(),
&funding_keys[j],
&libra_funding_keys[j],
&key.pubkey(),
amount,
blockhash,
);
let _sig = client
.async_send_transaction(tx.clone())
.expect("create_account in generate_and_fund_keypairs");
}
for (j, key) in keys.iter().enumerate() {
let mut times = 0;
loop {
let balance =
librapay_transaction::get_libra_balance(client, &key.pubkey()).unwrap();
if balance >= amount {
break;
} else if times > 20 {
info!("timed out.. {} key: {} balance: {}", i, j, balance);
break;
} else {
times += 1;
sleep(Duration::from_millis(100));
}
}
}
info!(
"funded group {} of {}",
i + 1,
keypairs.len() / NUM_FUNDING_KEYS
);
blockhash = get_recent_blockhash(client).0;
}
funding_time.stop();
info!("done funding keys, took {} ms", funding_time.as_ms());
}
pub fn generate_and_fund_keypairs<T: 'static + Client + Send + Sync>(
client: Arc<T>,
faucet_addr: Option<SocketAddr>,
funding_key: &Keypair,
keypair_count: usize,
lamports_per_account: u64,
use_move: bool,
) -> Result<(Vec<Keypair>, Option<LibraKeys>)> {
) -> Result<Vec<Keypair>> {
info!("Creating {} keypairs...", keypair_count);
let (mut keypairs, extra) = generate_keypairs(funding_key, keypair_count as u64);
info!("Get lamports...");
@ -1141,12 +885,6 @@ pub fn generate_and_fund_keypairs<T: 'static + Client + Send + Sync>(
let last_key = keypairs[keypair_count - 1].pubkey();
let last_keypair_balance = client.get_balance(&last_key).unwrap_or(0);
#[cfg(feature = "move")]
let mut move_keypairs_ret = None;
#[cfg(not(feature = "move"))]
let move_keypairs_ret = None;
// Repeated runs will eat up keypair balances from transaction fees. In order to quickly
// start another bench-tps run without re-funding all of the keypairs, check if the
// keypairs still have at least 80% of the expected funds. That should be enough to
@ -1157,10 +895,7 @@ pub fn generate_and_fund_keypairs<T: 'static + Client + Send + Sync>(
let max_fee = fee_rate_governor.max_lamports_per_signature;
let extra_fees = extra * max_fee;
let total_keypairs = keypairs.len() as u64 + 1; // Add one for funding keypair
let mut total = lamports_per_account * total_keypairs + extra_fees;
if use_move {
total *= 3;
}
let total = lamports_per_account * total_keypairs + extra_fees;
let funding_key_balance = client.get_balance(&funding_key.pubkey()).unwrap_or(0);
info!(
@ -1172,40 +907,6 @@ pub fn generate_and_fund_keypairs<T: 'static + Client + Send + Sync>(
airdrop_lamports(client.as_ref(), &faucet_addr.unwrap(), funding_key, total)?;
}
#[cfg(feature = "move")]
{
if use_move {
let libra_genesis_keypair =
create_genesis(&funding_key, client.as_ref(), 10_000_000);
let libra_mint_program_id = upload_mint_script(&funding_key, client.as_ref());
let libra_pay_program_id = upload_payment_script(&funding_key, client.as_ref());
// Generate another set of keypairs for move accounts.
// Still fund the solana ones which will be used for fees.
let seed = [0u8; 32];
let mut rnd = GenKeys::new(seed);
let move_keypairs = rnd.gen_n_keypairs(keypair_count as u64);
fund_move_keys(
client.as_ref(),
funding_key,
&move_keypairs,
total / 3,
&libra_pay_program_id,
&libra_mint_program_id,
&libra_genesis_keypair,
);
move_keypairs_ret = Some((
libra_genesis_keypair,
libra_pay_program_id,
libra_mint_program_id,
move_keypairs,
));
// Give solana keys 1/3 and move keys 1/3 the lamports. Keep 1/3 for fees.
total /= 3;
}
}
fund_keys(
client,
funding_key,
@ -1219,7 +920,7 @@ pub fn generate_and_fund_keypairs<T: 'static + Client + Send + Sync>(
// 'generate_keypairs' generates extra keys to be able to have size-aligned funding batches for fund_keys.
keypairs.truncate(keypair_count);
Ok((keypairs, move_keypairs_ret))
Ok(keypairs)
}
#[cfg(test)]
@ -1243,11 +944,11 @@ mod tests {
config.duration = Duration::from_secs(5);
let keypair_count = config.tx_count * config.keypair_multiplier;
let (keypairs, _move_keypairs) =
generate_and_fund_keypairs(client.clone(), None, &config.id, keypair_count, 20, false)
let keypairs =
generate_and_fund_keypairs(client.clone(), None, &config.id, keypair_count, 20)
.unwrap();
do_bench_tps(client, config, keypairs, None);
do_bench_tps(client, config, keypairs);
}
#[test]
@ -1258,9 +959,8 @@ mod tests {
let keypair_count = 20;
let lamports = 20;
let (keypairs, _move_keypairs) =
generate_and_fund_keypairs(client.clone(), None, &id, keypair_count, lamports, false)
.unwrap();
let keypairs =
generate_and_fund_keypairs(client.clone(), None, &id, keypair_count, lamports).unwrap();
for kp in &keypairs {
assert_eq!(
@ -1282,9 +982,8 @@ mod tests {
let keypair_count = 20;
let lamports = 20;
let (keypairs, _move_keypairs) =
generate_and_fund_keypairs(client.clone(), None, &id, keypair_count, lamports, false)
.unwrap();
let keypairs =
generate_and_fund_keypairs(client.clone(), None, &id, keypair_count, lamports).unwrap();
for kp in &keypairs {
assert_eq!(client.get_balance(&kp.pubkey()).unwrap(), lamports);

View File

@ -26,7 +26,6 @@ pub struct Config {
pub read_from_client_file: bool,
pub target_lamports_per_signature: u64,
pub multi_client: bool,
pub use_move: bool,
pub num_lamports_per_account: u64,
pub target_slots_per_epoch: u64,
pub target_node: Option<Pubkey>,
@ -50,7 +49,6 @@ impl Default for Config {
read_from_client_file: false,
target_lamports_per_signature: FeeRateGovernor::default().target_lamports_per_signature,
multi_client: true,
use_move: false,
num_lamports_per_account: NUM_LAMPORTS_PER_ACCOUNT_DEFAULT,
target_slots_per_epoch: 0,
target_node: None,
@ -114,11 +112,6 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
.long("sustained")
.help("Use sustained performance mode vs. peak mode. This overlaps the tx generation with transfers."),
)
.arg(
Arg::with_name("use-move")
.long("use-move")
.help("Use Move language transactions to perform transfers."),
)
.arg(
Arg::with_name("no-multi-client")
.long("no-multi-client")
@ -276,7 +269,6 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
args.target_lamports_per_signature = v.to_string().parse().expect("can't parse lamports");
}
args.use_move = matches.is_present("use-move");
args.multi_client = !matches.is_present("no-multi-client");
args.target_node = matches
.value_of("target_node")

View File

@ -29,7 +29,6 @@ fn main() {
write_to_client_file,
read_from_client_file,
target_lamports_per_signature,
use_move,
multi_client,
num_lamports_per_account,
target_node,
@ -100,7 +99,7 @@ fn main() {
Arc::new(get_client(&nodes))
};
let (keypairs, move_keypairs) = if *read_from_client_file && !use_move {
let keypairs = if *read_from_client_file {
let path = Path::new(&client_ids_and_stake_file);
let file = File::open(path).unwrap();
@ -130,7 +129,7 @@ fn main() {
// This prevents the amount of storage needed for bench-tps accounts from creeping up
// across multiple runs.
keypairs.sort_by_key(|x| x.pubkey().to_string());
(keypairs, None)
keypairs
} else {
generate_and_fund_keypairs(
client.clone(),
@ -138,7 +137,6 @@ fn main() {
&id,
keypair_count,
*num_lamports_per_account,
*use_move,
)
.unwrap_or_else(|e| {
eprintln!("Error could not fund keys: {:?}", e);
@ -146,5 +144,5 @@ fn main() {
})
};
do_bench_tps(client, cli_config, keypairs, move_keypairs);
do_bench_tps(client, cli_config, keypairs);
}

View File

@ -6,17 +6,11 @@ use solana_core::cluster_info::VALIDATOR_PORT_RANGE;
use solana_core::validator::ValidatorConfig;
use solana_faucet::faucet::run_local_faucet;
use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
#[cfg(feature = "move")]
use solana_sdk::move_loader::solana_move_loader_program;
use solana_sdk::signature::{Keypair, Signer};
use std::sync::{mpsc::channel, Arc};
use std::time::Duration;
fn test_bench_tps_local_cluster(config: Config) {
#[cfg(feature = "move")]
let native_instruction_processors = vec![solana_move_loader_program()];
#[cfg(not(feature = "move"))]
let native_instruction_processors = vec![];
solana_logger::setup();
@ -48,17 +42,16 @@ fn test_bench_tps_local_cluster(config: Config) {
let lamports_per_account = 100;
let keypair_count = config.tx_count * config.keypair_multiplier;
let (keypairs, move_keypairs) = generate_and_fund_keypairs(
let keypairs = generate_and_fund_keypairs(
client.clone(),
Some(faucet_addr),
&config.id,
keypair_count,
lamports_per_account,
config.use_move,
)
.unwrap();
let _total = do_bench_tps(client, config, keypairs, move_keypairs);
let _total = do_bench_tps(client, config, keypairs);
#[cfg(not(debug_assertions))]
assert!(_total > 100);
@ -73,14 +66,3 @@ fn test_bench_tps_local_cluster_solana() {
test_bench_tps_local_cluster(config);
}
#[test]
#[serial]
fn test_bench_tps_local_cluster_move() {
let mut config = Config::default();
config.tx_count = 100;
config.duration = Duration::from_secs(10);
config.use_move = true;
test_bench_tps_local_cluster(config);
}

View File

@ -16,6 +16,3 @@ steps:
timeout_in_minutes: 240
name: "publish crate"
branches: "!master"
# - command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-move.sh"
# name: "move"
# timeout_in_minutes: 20

View File

@ -1 +0,0 @@
test-stable.sh

View File

@ -38,7 +38,6 @@ echo "Executing $testName"
case $testName in
test-stable)
_ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
_ cargo +"$rust_stable" test --manifest-path bench-tps/Cargo.toml --features=move ${V:+--verbose} test_bench_tps_local_cluster_move -- --nocapture
;;
test-stable-perf)
# BPF program tests
@ -67,27 +66,6 @@ test-stable-perf)
_ cargo +"$rust_stable" test --package solana-perf --package solana-ledger --package solana-core --lib ${V:+--verbose} -- --nocapture
_ cargo +"$rust_stable" run --manifest-path poh-bench/Cargo.toml ${V:+--verbose} -- --hashes-per-tick 10
;;
test-move)
#ci/affects-files.sh \
# Cargo.lock$ \
# Cargo.toml$ \
# ^ci/rust-version.sh \
# ^ci/test-stable.sh \
# ^ci/test-move.sh \
# ^programs/move_loader \
# ^programs/librapay \
# ^logger/ \
# ^runtime/ \
# ^sdk/ \
#|| {
# annotate --style info \
# "Skipped $testName as no relevant files were modified"
# exit 0
#}
_ cargo +"$rust_stable" test --manifest-path programs/move_loader/Cargo.toml ${V:+--verbose} -- --nocapture
_ cargo +"$rust_stable" test --manifest-path programs/librapay/Cargo.toml ${V:+--verbose} -- --nocapture
exit 0
;;
test-local-cluster)
_ cargo +"$rust_stable" build --release --bins ${V:+--verbose}
_ cargo +"$rust_stable" test --release --package solana-local-cluster ${V:+--verbose} -- --nocapture --test-threads=1

View File

@ -143,7 +143,6 @@ module.exports = {
"implemented-proposals/repair-service",
"implemented-proposals/testing-programs",
"implemented-proposals/readonly-accounts",
"implemented-proposals/embedding-move",
"implemented-proposals/staking-rewards",
"implemented-proposals/rent",
"implemented-proposals/durable-tx-nonces",
@ -170,6 +169,7 @@ module.exports = {
"proposals/block-confirmation",
"proposals/rust-clients",
"proposals/optimistic_confirmation",
"proposals/embedding-move",
],
},
};

View File

@ -1,6 +1,5 @@
use solana_sdk::{
clock::Epoch, genesis_config::OperatingMode, inflation::Inflation,
move_loader::solana_move_loader_program, pubkey::Pubkey,
clock::Epoch, genesis_config::OperatingMode, inflation::Inflation, pubkey::Pubkey,
};
#[macro_use]
@ -57,7 +56,6 @@ pub fn get_programs(operating_mode: OperatingMode, epoch: Epoch) -> Option<Vec<(
// Programs that are only available in Development mode
solana_budget_program!(),
solana_exchange_program!(),
solana_move_loader_program(),
])
} else {
None
@ -135,7 +133,7 @@ mod tests {
fn test_development_programs() {
assert_eq!(
get_programs(OperatingMode::Development, 0).unwrap().len(),
5
4
);
assert_eq!(get_programs(OperatingMode::Development, 1), None);
}

View File

@ -1 +0,0 @@
/target/

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +0,0 @@
[package]
name = "solana-librapay"
version = "1.3.0"
description = "Solana Libra Payment"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
edition = "2018"
[dependencies]
bincode = "1.2.0"
log = "0.4.8"
solana-logger = { path = "../../logger", version = "1.3.0" }
solana-move-loader-program = { path = "../move_loader", version = "1.3.0" }
solana-runtime = { path = "../../runtime", version = "1.3.0" }
solana-sdk = { path = "../../sdk", version = "1.3.0" }
types = { version = "0.0.1-sol5", package = "solana_libra_types" }
[lib]
crate-type = ["lib", "cdylib"]
name = "solana_librapay"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -1,85 +0,0 @@
solana_sdk::declare_id!("LibraPay11111111111111111111111111111111111");
pub mod librapay_instruction;
pub mod librapay_transaction;
extern crate solana_move_loader_program;
use solana_move_loader_program::account_state::LibraAccountState;
use solana_runtime::loader_utils::load_program;
use solana_sdk::{
client::Client,
message::Message,
pubkey::Pubkey,
signature::{Keypair, Signer},
system_instruction,
};
use types::account_config;
pub fn create_genesis<T: Client>(from: &Keypair, client: &T, amount: u64) -> Keypair {
let genesis = Keypair::new();
let instruction = system_instruction::create_account(
&from.pubkey(),
&genesis.pubkey(),
1,
bincode::serialized_size(&LibraAccountState::create_genesis(amount).unwrap()).unwrap()
as u64,
&solana_sdk::move_loader::id(),
);
client
.send_and_confirm_message(
&[&from, &genesis],
Message::new(&[instruction], Some(&from.pubkey())),
)
.unwrap();
let instruction = librapay_instruction::genesis(&genesis.pubkey(), amount);
let message = Message::new(&[instruction], Some(&from.pubkey()));
client
.send_and_confirm_message(&[from, &genesis], message)
.unwrap();
genesis
}
pub fn publish_module<T: Client>(from: &Keypair, client: &T, code: &str) -> Pubkey {
let address = account_config::association_address();
let account_state = LibraAccountState::create_module(&address, code, vec![]);
let bytes = bincode::serialize(&account_state).unwrap();
load_program(client, &from, &solana_sdk::move_loader::id(), bytes)
}
pub fn upload_script<T: Client>(from: &Keypair, client: &T, code: &str) -> Pubkey {
let address = account_config::association_address();
let account_state = LibraAccountState::create_script(&address, code, vec![]);
let bytes = bincode::serialize(&account_state).unwrap();
load_program(client, &from, &solana_sdk::move_loader::id(), bytes)
}
pub fn upload_mint_script<T: Client>(from: &Keypair, client: &T) -> Pubkey {
let code = "
import 0x0.LibraAccount;
import 0x0.LibraCoin;
main(payee: address, amount: u64) {
LibraAccount.mint_to_address(move(payee), move(amount));
return;
}";
upload_script(from, client, code)
}
pub fn upload_payment_script<T: Client>(from: &Keypair, client: &T) -> Pubkey {
let code = "
import 0x0.LibraAccount;
import 0x0.LibraCoin;
main(payee: address, amount: u64) {
LibraAccount.pay_from_sender(move(payee), move(amount));
return;
}
";
upload_script(from, client, code)
}

View File

@ -1,92 +0,0 @@
use solana_move_loader_program::{
account_state::pubkey_to_address,
processor::{Executable, MoveLoaderInstruction},
};
use solana_sdk::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
};
use types::{account_config, transaction::TransactionArgument};
pub fn genesis(genesis_pubkey: &Pubkey, microlibras: u64) -> Instruction {
let instruction_data = MoveLoaderInstruction::CreateGenesis(microlibras);
let accounts = vec![AccountMeta::new(*genesis_pubkey, true)];
Instruction::new(solana_sdk::move_loader::id(), &instruction_data, accounts)
}
pub fn mint(
script_pubkey: &Pubkey,
genesis_pubkey: &Pubkey,
to_pubkey: &Pubkey,
microlibras: u64,
) -> Instruction {
let args = vec![
TransactionArgument::Address(pubkey_to_address(to_pubkey)),
TransactionArgument::U64(microlibras),
];
let instruction_data = Executable::RunScript {
sender_address: account_config::association_address(),
function_name: "main".to_string(),
args,
};
let accounts = vec![
AccountMeta::new_readonly(*script_pubkey, false),
AccountMeta::new(*genesis_pubkey, true),
AccountMeta::new(*to_pubkey, false),
];
Instruction::new(solana_sdk::move_loader::id(), &instruction_data, accounts)
}
pub fn transfer(
script_pubkey: &Pubkey,
genesis_pubkey: &Pubkey,
from_pubkey: &Pubkey,
to_pubkey: &Pubkey,
microlibras: u64,
) -> Instruction {
let args = vec![
TransactionArgument::Address(pubkey_to_address(to_pubkey)),
TransactionArgument::U64(microlibras),
];
let instruction_data = Executable::RunScript {
sender_address: pubkey_to_address(from_pubkey),
function_name: "main".to_string(),
args,
};
let accounts = vec![
AccountMeta::new_readonly(*script_pubkey, false),
AccountMeta::new_readonly(*genesis_pubkey, false),
AccountMeta::new(*from_pubkey, true),
AccountMeta::new(*to_pubkey, false),
];
Instruction::new(solana_sdk::move_loader::id(), &instruction_data, accounts)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pay() {
let from_pubkey = Pubkey::new_rand();
let to_pubkey = Pubkey::new_rand();
let program_id = Pubkey::new_rand();
let mint_id = Pubkey::new_rand();
transfer(&program_id, &mint_id, &from_pubkey, &to_pubkey, 1);
}
#[test]
fn test_mint() {
let program_id = Pubkey::new_rand();
let from_pubkey = Pubkey::new_rand();
let to_pubkey = Pubkey::new_rand();
mint(&program_id, &from_pubkey, &to_pubkey, 1);
}
}

View File

@ -1,233 +0,0 @@
use crate::librapay_instruction;
use log::*;
use solana_move_loader_program::{
account_state::{pubkey_to_address, LibraAccountState},
data_store::DataStore,
};
use solana_sdk::{
client::Client,
commitment_config::CommitmentConfig,
hash::Hash,
message::Message,
pubkey::Pubkey,
signature::{Keypair, Signer},
system_instruction,
transaction::Transaction,
};
use std::{boxed::Box, error, fmt};
pub fn create_genesis(keypair: &Keypair, microlibras: u64, recent_blockhash: Hash) -> Transaction {
let ix = librapay_instruction::genesis(&keypair.pubkey(), microlibras);
Transaction::new_signed_with_payer(&[ix], Some(&keypair.pubkey()), &[keypair], recent_blockhash)
}
pub fn mint_tokens(
script_pubkey: &Pubkey,
payer_keypair: &Keypair,
genesis_keypair: &Keypair,
to_pubkey: &Pubkey,
microlibras: u64,
recent_blockhash: Hash,
) -> Transaction {
let ix = librapay_instruction::mint(
script_pubkey,
&genesis_keypair.pubkey(),
to_pubkey,
microlibras,
);
Transaction::new_signed_with_payer(
&[ix],
Some(&payer_keypair.pubkey()),
&[payer_keypair, genesis_keypair],
recent_blockhash,
)
}
pub fn transfer(
script_pubkey: &Pubkey,
genesis_pubkey: &Pubkey,
payer_keypair: &Keypair,
from_keypair: &Keypair,
to_pubkey: &Pubkey,
microlibras: u64,
recent_blockhash: Hash,
) -> Transaction {
let ix = librapay_instruction::transfer(
script_pubkey,
genesis_pubkey,
&from_keypair.pubkey(),
to_pubkey,
microlibras,
);
Transaction::new_signed_with_payer(
&[ix],
Some(&payer_keypair.pubkey()),
&[payer_keypair, from_keypair],
recent_blockhash,
)
}
pub fn create_accounts(
from_keypair: &Keypair,
to_keypair: &[&Keypair],
lamports: u64,
recent_blockhash: Hash,
) -> Transaction {
let instructions: Vec<_> = to_keypair
.iter()
.map(|keypair| {
system_instruction::create_account(
&from_keypair.pubkey(),
&keypair.pubkey(),
lamports,
400,
&solana_sdk::move_loader::id(),
)
})
.collect();
let mut from_signers = vec![from_keypair];
from_signers.extend_from_slice(to_keypair);
let message = Message::new(&instructions, Some(&from_keypair.pubkey()));
Transaction::new(&from_signers, message, recent_blockhash)
}
pub fn create_account(
from_keypair: &Keypair,
to_keypair: &Keypair,
lamports: u64,
recent_blockhash: Hash,
) -> Transaction {
create_accounts(from_keypair, &[to_keypair], lamports, recent_blockhash)
}
#[derive(Debug)]
enum LibrapayError {
UnknownAccountState,
}
impl fmt::Display for LibrapayError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl error::Error for LibrapayError {}
pub fn get_libra_balance<T: Client>(
client: &T,
pubkey: &Pubkey,
) -> Result<u64, Box<dyn error::Error>> {
if let Some(account) =
client.get_account_with_commitment(&pubkey, CommitmentConfig::recent())?
{
let mut data_store = DataStore::default();
match bincode::deserialize(&account.data)? {
LibraAccountState::User(_, write_set) => {
data_store.apply_write_set(&write_set);
}
LibraAccountState::Unallocated => {
return Ok(0);
}
state => {
info!("Unknown account state: {:?}", state);
return Err(LibrapayError::UnknownAccountState.into());
}
}
let resource = data_store
.read_account_resource(&pubkey_to_address(pubkey))
.unwrap();
let res = resource.balance();
Ok(res)
} else {
Ok(0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{create_genesis, upload_mint_script, upload_payment_script};
use solana_runtime::bank::Bank;
use solana_runtime::bank_client::BankClient;
use solana_sdk::genesis_config::create_genesis_config;
use solana_sdk::signature::{Keypair, Signer};
use std::sync::Arc;
fn create_bank(lamports: u64) -> (Arc<Bank>, Keypair, Keypair, Pubkey, Pubkey) {
let (mut genesis_config, mint) = create_genesis_config(lamports);
genesis_config.rent.lamports_per_byte_year = 0;
let bank = Bank::new(&genesis_config);
bank.add_native_program("solana_move_loader_program", &solana_sdk::move_loader::id());
let shared_bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&shared_bank);
let genesis_pubkey = create_genesis(&mint, &bank_client, 1_000_000);
let mint_script_pubkey = upload_mint_script(&mint, &bank_client);
let script_pubkey = upload_payment_script(&mint, &bank_client);
(
shared_bank,
mint,
genesis_pubkey,
mint_script_pubkey,
script_pubkey,
)
}
#[test]
fn test_transfer() {
solana_logger::setup();
let (bank, mint, genesis_keypair, mint_script_pubkey, payment_script_pubkey) =
create_bank(10_000);
let from_keypair = Keypair::new();
let to_keypair = Keypair::new();
let tx = create_accounts(
&mint,
&[&from_keypair, &to_keypair],
1,
bank.last_blockhash(),
);
bank.process_transaction(&tx).unwrap();
info!(
"created accounts: mint: {} genesis_pubkey: {}",
mint.pubkey(),
genesis_keypair.pubkey()
);
info!(
" from: {} to: {}",
from_keypair.pubkey(),
to_keypair.pubkey()
);
let tx = mint_tokens(
&mint_script_pubkey,
&mint,
&genesis_keypair,
&from_keypair.pubkey(),
1,
bank.last_blockhash(),
);
bank.process_transaction(&tx).unwrap();
let client = BankClient::new_shared(&bank);
assert_eq!(
1,
get_libra_balance(&client, &from_keypair.pubkey()).unwrap()
);
info!("passed mint... doing another transfer..");
let tx = transfer(
&payment_script_pubkey,
&genesis_keypair.pubkey(),
&mint,
&from_keypair,
&to_keypair.pubkey(),
1,
bank.last_blockhash(),
);
bank.process_transaction(&tx).unwrap();
assert_eq!(1, get_libra_balance(&client, &to_keypair.pubkey()).unwrap());
}
}

View File

@ -1 +0,0 @@
/target/

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +0,0 @@
[package]
name = "solana-move-loader-program"
version = "1.3.0"
description = "Solana Move loader"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
edition = "2018"
[dependencies]
bincode = "1.2.0"
indexmap = "1.1.0"
log = "0.4.8"
serde = "1.0.104"
serde_bytes = "0.11"
serde_derive = "1.0.103"
serde_json = "1.0.46"
solana-logger = { path = "../../logger", version = "1.3.0" }
solana-sdk = { path = "../../sdk", version = "1.3.0" }
bytecode_verifier = { version = "0.0.1-sol5", package = "solana_libra_bytecode_verifier" }
canonical_serialization = { version = "0.0.1-sol5", package = "solana_libra_canonical_serialization" }
compiler = { version = "0.0.1-sol5", package = "solana_libra_compiler" }
failure = { version = "0.0.1-sol5", package = "solana_libra_failure_ext" }
num-derive = { version = "0.3" }
num-traits = { version = "0.2" }
state_view = { version = "0.0.1-sol5", package = "solana_libra_state_view" }
stdlib = { version = "0.0.1-sol5", package = "solana_libra_stdlib" }
thiserror = "1.0"
types = { version = "0.0.1-sol5", package = "solana_libra_types" }
vm = { version = "0.0.1-sol5", package = "solana_libra_vm" }
vm_cache_map = { version = "0.0.1-sol5", package = "solana_libra_vm_cache_map" }
vm_runtime = { version = "0.0.1-sol5", package = "solana_libra_vm_runtime" }
vm_runtime_types = { version = "0.0.1-sol5", package = "solana_libra_vm_runtime_types" }
[lib]
crate-type = ["lib", "cdylib"]
name = "solana_move_loader_program"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -1,201 +0,0 @@
use crate::data_store::DataStore;
use crate::error_mappers::*;
use bytecode_verifier::VerifiedModule;
use compiler::Compiler;
use serde_derive::{Deserialize, Serialize};
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey};
use std::convert::TryInto;
use stdlib::stdlib_modules;
use types::{
account_address::AccountAddress,
account_config,
identifier::Identifier,
transaction::{Module, Script},
write_set::{WriteOp, WriteSet},
};
use vm::{
access::ModuleAccess, file_format::CompiledModule, transaction_metadata::TransactionMetadata,
};
use vm_cache_map::Arena;
use vm_runtime::{
code_cache::{
module_adapter::FakeFetcher,
module_cache::{BlockModuleCache, VMModuleCache},
},
data_cache::BlockDataCache,
txn_executor::{TransactionExecutor, ACCOUNT_MODULE, BLOCK_MODULE, COIN_MODULE},
};
use vm_runtime_types::value::Value;
// Helper function that converts a Solana Pubkey to a Libra AccountAddress (WIP)
pub fn pubkey_to_address(key: &Pubkey) -> AccountAddress {
AccountAddress::new(*to_array_32(key.as_ref()))
}
fn to_array_32(array: &[u8]) -> &[u8; 32] {
array.try_into().expect("slice with incorrect length")
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ModuleBytes {
#[serde(with = "serde_bytes")]
pub bytes: Vec<u8>,
}
/// Type of Libra account held by a Solana account
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum LibraAccountState {
/// No data for this account yet
Unallocated,
/// Json string representation of types::transaction::Module
CompiledScript(String),
/// Json string representation of types::transaction::Script
CompiledModule(String),
/// Serialized verified script bytes
VerifiedScript {
#[serde(with = "serde_bytes")]
script_bytes: Vec<u8>,
},
// Write set containing the published module
PublishedModule(WriteSet),
/// Associated genesis account and the write set containing the Libra account data
User(Pubkey, WriteSet),
/// Write sets containing the mint and stdlib modules
Genesis(WriteSet),
}
impl LibraAccountState {
pub fn create_unallocated() -> Self {
Self::Unallocated
}
fn create_compiler(sender_address: &AccountAddress, deps: Vec<&Vec<u8>>) -> Compiler {
// Compiler needs all the dependencies and the dependency module's account's
// data into `VerifiedModules`
let mut extra_deps: Vec<VerifiedModule> = vec![];
for dep in deps {
let state: Self = bincode::deserialize(&dep).unwrap();
if let Self::PublishedModule(write_set) = state {
for (_, write_op) in write_set.iter() {
if let WriteOp::Value(raw_bytes) = write_op {
let v =
VerifiedModule::new(CompiledModule::deserialize(&raw_bytes).unwrap())
.unwrap();
extra_deps.push(v);
}
}
}
}
Compiler {
address: *sender_address,
extra_deps,
..Compiler::default()
}
}
pub fn create_script(sender_address: &AccountAddress, code: &str, deps: Vec<&Vec<u8>>) -> Self {
let compiler = Self::create_compiler(sender_address, deps);
let compiled_script = compiler.into_script(code).expect("Failed to compile");
let mut script_bytes = vec![];
compiled_script
.serialize(&mut script_bytes)
.expect("Unable to serialize script");
// TODO args?
Self::CompiledScript(serde_json::to_string(&Script::new(script_bytes, vec![])).unwrap())
}
pub fn create_module(sender_address: &AccountAddress, code: &str, deps: Vec<&Vec<u8>>) -> Self {
let compiler = Self::create_compiler(sender_address, deps);
let compiled_module = compiler
.into_compiled_module(code)
.expect("Failed to compile");
let mut module_bytes = vec![];
compiled_module
.serialize(&mut module_bytes)
.expect("Unable to serialize script");
Self::CompiledModule(serde_json::to_string(&Module::new(module_bytes)).unwrap())
}
pub fn create_user(owner: &Pubkey, write_set: WriteSet) -> Self {
Self::User(*owner, write_set)
}
pub fn create_genesis(mint_balance: u64) -> Result<Self, InstructionError> {
let modules = stdlib_modules();
let arena = Arena::new();
let state_view = DataStore::default();
let vm_cache = VMModuleCache::new(&arena);
let genesis_addr = account_config::association_address();
let write_set = {
let fake_fetcher =
FakeFetcher::new(modules.iter().map(|m| m.as_inner().clone()).collect());
let data_cache = BlockDataCache::new(&state_view);
let block_cache = BlockModuleCache::new(&vm_cache, fake_fetcher);
let mut txn_data = TransactionMetadata::default();
txn_data.sender = genesis_addr;
let mut txn_executor = TransactionExecutor::new(&block_cache, &data_cache, txn_data);
txn_executor.create_account(genesis_addr).unwrap();
txn_executor
.create_account(account_config::core_code_address())
.map_err(map_err_vm_status)?;
txn_executor
.execute_function(
&BLOCK_MODULE,
&Identifier::new("initialize").unwrap(),
vec![],
)
.map_err(map_err_vm_status)?;
txn_executor
.execute_function(
&COIN_MODULE,
&Identifier::new("initialize").unwrap(),
vec![],
)
.map_err(map_err_vm_status)?;
txn_executor
.execute_function(
&ACCOUNT_MODULE,
&Identifier::new("mint_to_address").unwrap(),
vec![Value::address(genesis_addr), Value::u64(mint_balance)],
)
.map_err(map_err_vm_status)?;
// Bump the sequence number for the Association account. If we don't do this and a
// subsequent transaction (e.g., minting) is sent from the Association account, a problem
// arises: both the genesis transaction and the subsequent transaction have sequence
// number 0
txn_executor
.execute_function(
&ACCOUNT_MODULE,
&Identifier::new("epilogue").unwrap(),
vec![],
)
.map_err(map_err_vm_status)?;
let mut stdlib_modules = vec![];
for module in modules.iter() {
let mut buf = vec![];
module.serialize(&mut buf).map_err(map_failure_error)?;
stdlib_modules.push((module.self_id(), buf));
}
txn_executor
.make_write_set(stdlib_modules, Ok(()))
.map_err(map_err_vm_status)?
.write_set()
.clone()
.into_mut()
}
.freeze()
.map_err(map_failure_error)?;
Ok(Self::Genesis(write_set))
}
}

View File

@ -1,177 +0,0 @@
use bytecode_verifier::verifier::VerifiedModule;
use canonical_serialization::SimpleDeserializer;
use failure::prelude::*;
use indexmap::IndexMap;
use log::*;
use state_view::StateView;
use types::{
access_path::AccessPath,
account_address::AccountAddress,
account_config::{self, AccountResource},
write_set::{WriteOp, WriteSet, WriteSetMut},
};
use vm::access::ModuleAccess;
use vm::errors::VMResult;
use vm_runtime::{data_cache::RemoteCache, identifier::create_access_path};
/// An in-memory implementation of [`StateView`] and [`RemoteCache`] for the VM.
#[derive(Debug, Default)]
pub struct DataStore {
data: IndexMap<AccessPath, Vec<u8>>,
}
impl DataStore {
/// Creates a new `DataStore` with the provided initial data.
pub fn new(data: IndexMap<AccessPath, Vec<u8>>) -> Self {
Self { data }
}
/// Applies a [`WriteSet`] to this data store.
pub fn apply_write_set(&mut self, write_set: &WriteSet) {
for (access_path, write_op) in write_set {
match write_op {
WriteOp::Value(value) => {
self.set(access_path.clone(), value.clone());
}
WriteOp::Deletion => {
self.remove(access_path);
}
}
}
}
/// Returns a `WriteSet` for each account in the `DataStore`
pub fn into_write_sets(mut self) -> Result<IndexMap<AccountAddress, WriteSet>> {
let mut write_set_muts: IndexMap<AccountAddress, WriteSetMut> = IndexMap::new();
for (access_path, value) in self.data.drain(..) {
match write_set_muts.get_mut(&access_path.address) {
Some(write_set_mut) => write_set_mut.push((access_path, WriteOp::Value(value))),
None => {
write_set_muts.insert(
access_path.address,
WriteSetMut::new(vec![(access_path, WriteOp::Value(value))]),
);
}
}
}
// Freeze each WriteSet
let mut write_sets: IndexMap<AccountAddress, WriteSet> = IndexMap::new();
for (address, write_set_mut) in write_set_muts.drain(..) {
write_sets.insert(address, write_set_mut.freeze()?);
}
Ok(write_sets)
}
/// Read an account's resource
pub fn read_account_resource(&self, addr: &AccountAddress) -> Option<AccountResource> {
let access_path = create_access_path(&addr, account_config::account_struct_tag());
self.data
.get(&access_path)
.and_then(|blob| SimpleDeserializer::deserialize(blob).ok())
}
/// Sets a (key, value) pair within this data store.
///
/// Returns the previous data if the key was occupied.
pub fn set(&mut self, access_path: AccessPath, data_blob: Vec<u8>) -> Option<Vec<u8>> {
self.data.insert(access_path, data_blob)
}
/// Deletes a key from this data store.
///
/// Returns the previous data if the key was occupied.
pub fn remove(&mut self, access_path: &AccessPath) -> Option<Vec<u8>> {
self.data.remove(access_path)
}
/// Adds a [`VerifiedModule`] to this data store.
pub fn add_module(&mut self, module: &VerifiedModule) -> Result<()> {
let access_path = AccessPath::from(&module.self_id());
let mut bytes = vec![];
module.serialize(&mut bytes)?;
self.set(access_path, bytes);
Ok(())
}
/// Dumps the data store to stdout
pub fn dump(&self) {
trace!("Data store:");
for (access_path, value) in &self.data {
trace!("{:?}: {:?}", access_path, value.len());
}
}
}
impl StateView for DataStore {
fn get(&self, access_path: &AccessPath) -> Result<Option<Vec<u8>>> {
// Since the data is in-memory, it can't fail.
match self.data.get(access_path) {
None => Ok(None),
Some(value) => Ok(Some(value.clone())),
}
}
fn multi_get(&self, _access_paths: &[AccessPath]) -> Result<Vec<Option<Vec<u8>>>> {
unimplemented!();
}
fn is_genesis(&self) -> bool {
false
}
}
impl RemoteCache for DataStore {
fn get(&self, access_path: &AccessPath) -> VMResult<Option<Vec<u8>>> {
Ok(StateView::get(self, access_path).expect("it should not error"))
}
}
#[cfg(test)]
mod tests {
use super::*;
use types::account_address::ADDRESS_LENGTH;
#[test]
fn test_write_set_order() {
solana_logger::setup();
let mut data_store = DataStore::default();
let address1 = AccountAddress::new([0; ADDRESS_LENGTH]);
let address2 = AccountAddress::new([1; ADDRESS_LENGTH]);
let address3 = AccountAddress::new([2; ADDRESS_LENGTH]);
let mut before1 = WriteSetMut::default();
let mut before2 = WriteSetMut::default();
let mut before3 = WriteSetMut::default();
for i in 1..10 {
before1.push((
AccessPath::new(address1, AccountAddress::random().to_vec()),
WriteOp::Value(vec![i]),
));
before2.push((
AccessPath::new(address2, AccountAddress::random().to_vec()),
WriteOp::Value(vec![i]),
));
before3.push((
AccessPath::new(address3, AccountAddress::random().to_vec()),
WriteOp::Value(vec![i]),
));
}
let before1 = before1.freeze().unwrap();
let before2 = before2.freeze().unwrap();
let before3 = before3.freeze().unwrap();
data_store.apply_write_set(&before1);
data_store.apply_write_set(&before2);
data_store.apply_write_set(&before3);
let write_sets = data_store.into_write_sets().unwrap();
let after1 = write_sets.get(&address1).unwrap();
let after2 = write_sets.get(&address2).unwrap();
let after3 = write_sets.get(&address3).unwrap();
assert_eq!(&before1, after1);
assert_eq!(&before2, after2);
assert_eq!(&before3, after3);
}
}

View File

@ -1,47 +0,0 @@
//! Mapping functions to Instruction errors
use log::*;
use solana_sdk::instruction::InstructionError;
use std::convert::TryInto;
use types::vm_error::{StatusCode, VMStatus};
use vm::file_format::CompiledModule;
#[allow(clippy::needless_pass_by_value)]
pub fn map_data_error(err: std::boxed::Box<bincode::ErrorKind>) -> InstructionError {
debug!("Error: Account data: {:?}", err);
match err.as_ref() {
bincode::ErrorKind::SizeLimit => InstructionError::AccountDataTooSmall,
_ => InstructionError::InvalidAccountData,
}
}
#[allow(clippy::needless_pass_by_value)]
pub fn map_json_error(err: serde_json::error::Error) -> InstructionError {
debug!("Error: serde_json: {:?}", err);
InstructionError::InvalidAccountData
}
pub fn map_vm_verification_error(err: (CompiledModule, Vec<VMStatus>)) -> InstructionError {
debug!("Error: Script verification failed: {:?}", err.1);
InstructionError::InvalidInstructionData
}
#[allow(clippy::needless_pass_by_value)]
pub fn missing_account() -> InstructionError {
debug!("Error: Missing account");
InstructionError::InvalidAccountData
}
pub fn map_failure_error(err: failure::Error) -> InstructionError {
debug!("Error: Script verification failed: {:?}", err);
InstructionError::InvalidInstructionData
}
pub fn map_err_vm_status(status: VMStatus) -> InstructionError {
// Attempt to map the StatusCode (repr(u64)) to a u32 for Custom.
// The only defined StatusCode that fails is StatusCode::UNKNOWN_ERROR
match <StatusCode as Into<u64>>::into(status.major_status).try_into() {
Ok(u) => InstructionError::Custom(u),
Err(_) => InstructionError::InvalidError,
}
}

View File

@ -1,12 +0,0 @@
pub mod account_state;
pub mod data_store;
pub mod error_mappers;
pub mod processor;
use crate::processor::MoveProcessor;
solana_sdk::declare_loader!(
solana_sdk::move_loader::ID,
solana_move_loader_program,
MoveProcessor::process_instruction
);

View File

@ -1,918 +0,0 @@
use crate::{
account_state::{pubkey_to_address, LibraAccountState},
data_store::DataStore,
error_mappers::*,
};
use bincode::config::Options;
use bytecode_verifier::verifier::{VerifiedModule, VerifiedScript};
use log::*;
use num_derive::{FromPrimitive, ToPrimitive};
use serde_derive::{Deserialize, Serialize};
use solana_sdk::{
account::{is_executable, next_keyed_account, KeyedAccount},
account_utils::State,
decode_error::DecodeError,
entrypoint_native::InvokeContext,
instruction::InstructionError,
move_loader::id,
program_utils::limited_deserialize,
pubkey::Pubkey,
};
use thiserror::Error;
use types::{
account_address::AccountAddress,
account_config,
identifier::Identifier,
transaction::{Module, Script, TransactionArgument, TransactionOutput},
};
use vm::{
access::ModuleAccess,
file_format::{CompiledModule, CompiledScript},
gas_schedule::{MAXIMUM_NUMBER_OF_GAS_UNITS, MAX_PRICE_PER_GAS_UNIT},
transaction_metadata::TransactionMetadata,
vm_string::VMString,
};
use vm_cache_map::Arena;
use vm_runtime::{
code_cache::{
module_adapter::ModuleFetcherImpl,
module_cache::{BlockModuleCache, ModuleCache, VMModuleCache},
},
txn_executor::TransactionExecutor,
};
use vm_runtime_types::value::Value;
#[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)]
pub enum MoveError {
#[error("Invalid Move data store")]
InvalidDataStore,
#[error("Invaid Move script")]
InvalidScript,
}
impl<T> DecodeError<T> for MoveError {
fn type_of() -> &'static str {
"MoveError"
}
}
/// Instruction data passed to perform a loader operation, must be based
/// on solana_sdk::loader_instruction::LoaderInstruction
#[derive(Serialize, Deserialize, Debug)]
pub enum MoveLoaderInstruction {
/// Write program data into an account
///
/// * key[0] - the account to write into.
///
/// The transaction must be signed by key[0]
Write {
offset: u32,
#[serde(with = "serde_bytes")]
bytes: Vec<u8>,
},
/// Finalize an account loaded with program data for execution.
/// The exact preparation steps is loader specific but on success the loader must set the executable
/// bit of the Account
///
/// * key[0] - the account to prepare for execution
///
/// The transaction must be signed by key[0]
Finalize,
/// Create a new genesis account
///
/// * key[0] - the account to write the genesis into
///
/// The transaction must be signed by key[0]
CreateGenesis(u64),
}
/// Instruction data passed when executing a Move script
#[derive(Serialize, Deserialize, Debug)]
pub enum Executable {
RunScript {
/// Sender of the "transaction", the "sender" who is running this script
sender_address: AccountAddress,
/// Name of the script's function to call
function_name: String,
/// Arguments to pass to the script being called
args: Vec<TransactionArgument>,
},
}
pub struct MoveProcessor {}
impl MoveProcessor {
#[allow(clippy::needless_pass_by_value)]
fn missing_account() -> InstructionError {
debug!("Error: Missing libra account");
InstructionError::InvalidAccountData
}
fn arguments_to_values(args: Vec<TransactionArgument>) -> Vec<Value> {
let mut locals = vec![];
for arg in args {
locals.push(match arg {
TransactionArgument::U64(i) => Value::u64(i),
TransactionArgument::Address(a) => Value::address(a),
TransactionArgument::ByteArray(b) => Value::byte_array(b),
TransactionArgument::String(s) => Value::string(VMString::new(s)),
});
}
locals
}
fn serialize_and_enforce_length(
state: &LibraAccountState,
data: &mut Vec<u8>,
) -> Result<(), InstructionError> {
let original_len = data.len() as u64;
let mut writer = std::io::Cursor::new(data);
bincode::options()
.with_limit(original_len)
.with_fixint_encoding()
.allow_trailing_bytes()
.serialize_into(&mut writer, &state)
.map_err(map_data_error)?;
if writer.position() < original_len {
writer.set_position(original_len);
}
Ok(())
}
fn deserialize_verified_script(data: &[u8]) -> Result<VerifiedScript, InstructionError> {
match limited_deserialize(data)? {
LibraAccountState::VerifiedScript { script_bytes } => {
let script =
VerifiedScript::deserialize(&script_bytes).map_err(map_err_vm_status)?;
Ok(script)
}
_ => {
debug!("Error: Script account does not contain a script");
Err(InstructionError::InvalidArgument)
}
}
}
fn execute(
sender_address: AccountAddress,
function_name: &str,
args: Vec<TransactionArgument>,
script: VerifiedScript,
data_store: &DataStore,
) -> Result<TransactionOutput, InstructionError> {
let allocator = Arena::new();
let code_cache = VMModuleCache::new(&allocator);
let module_cache = BlockModuleCache::new(&code_cache, ModuleFetcherImpl::new(data_store));
let modules_to_publish = vec![];
let main_module = script.into_module();
let module_id = main_module.self_id();
module_cache.cache_module(main_module);
let mut txn_metadata = TransactionMetadata::default();
txn_metadata.sender = sender_address;
// Caps execution to the Libra prescribed 10 milliseconds
txn_metadata.max_gas_amount = *MAXIMUM_NUMBER_OF_GAS_UNITS;
txn_metadata.gas_unit_price = *MAX_PRICE_PER_GAS_UNIT;
let mut vm = TransactionExecutor::new(&module_cache, data_store, txn_metadata);
vm.execute_function(
&module_id,
&Identifier::new(function_name).unwrap(),
Self::arguments_to_values(args),
)
.map_err(map_err_vm_status)?;
Ok(vm
.make_write_set(modules_to_publish, Ok(()))
.map_err(map_err_vm_status)?)
}
fn keyed_accounts_to_data_store(
keyed_accounts: &[KeyedAccount],
) -> Result<DataStore, InstructionError> {
let mut data_store = DataStore::default();
let mut keyed_accounts_iter = keyed_accounts.iter();
let genesis = next_keyed_account(&mut keyed_accounts_iter)?;
match limited_deserialize(&genesis.try_account_ref()?.data)? {
LibraAccountState::Genesis(write_set) => data_store.apply_write_set(&write_set),
_ => {
debug!("Must include a genesis account");
return Err(InstructionError::InvalidArgument);
}
}
for keyed_account in keyed_accounts_iter {
match limited_deserialize(&keyed_account.try_account_ref()?.data)? {
LibraAccountState::User(owner, write_set) => {
if owner != *genesis.unsigned_key() {
debug!("User account must be owned by this genesis");
return Err(InstructionError::InvalidArgument);
}
data_store.apply_write_set(&write_set)
}
LibraAccountState::PublishedModule(write_set) => {
data_store.apply_write_set(&write_set)
}
_ => (),
}
}
Ok(data_store)
}
fn data_store_to_keyed_accounts(
data_store: DataStore,
keyed_accounts: &[KeyedAccount],
) -> Result<(), InstructionError> {
let mut write_sets = data_store
.into_write_sets()
.map_err(|_| MoveError::InvalidDataStore)?;
let mut keyed_accounts_iter = keyed_accounts.iter();
// Genesis account holds both mint and stdlib
let genesis = next_keyed_account(&mut keyed_accounts_iter)?;
let genesis_key = *genesis.unsigned_key();
let mut write_set = write_sets
.remove(&account_config::association_address())
.ok_or_else(Self::missing_account)?
.into_mut();
for (access_path, write_op) in write_sets
.remove(&account_config::core_code_address())
.ok_or_else(Self::missing_account)?
.into_iter()
{
write_set.push((access_path, write_op));
}
let write_set = write_set.freeze().unwrap();
Self::serialize_and_enforce_length(
&LibraAccountState::Genesis(write_set),
&mut genesis.try_account_ref_mut()?.data,
)?;
// Now do the rest of the accounts
for keyed_account in keyed_accounts_iter {
let write_set = write_sets
.remove(&pubkey_to_address(keyed_account.unsigned_key()))
.ok_or_else(Self::missing_account)?;
if !keyed_account.executable()? {
// Only write back non-executable accounts
Self::serialize_and_enforce_length(
&LibraAccountState::User(genesis_key, write_set),
&mut keyed_account.try_account_ref_mut()?.data,
)?;
}
}
if !write_sets.is_empty() {
debug!("Error: Missing keyed accounts");
return Err(InstructionError::NotEnoughAccountKeys);
}
Ok(())
}
pub fn do_write(
keyed_accounts: &[KeyedAccount],
offset: u32,
bytes: &[u8],
) -> Result<(), InstructionError> {
let mut keyed_accounts_iter = keyed_accounts.iter();
let keyed_account = next_keyed_account(&mut keyed_accounts_iter)?;
if keyed_account.signer_key().is_none() {
debug!("Error: key[0] did not sign the transaction");
return Err(InstructionError::MissingRequiredSignature);
}
let offset = offset as usize;
let len = bytes.len();
trace!("Write: offset={} length={}", offset, len);
if keyed_account.data_len()? < offset + len {
debug!(
"Error: Write overflow: {} < {}",
keyed_account.data_len()?,
offset + len
);
return Err(InstructionError::AccountDataTooSmall);
}
keyed_account.try_account_ref_mut()?.data[offset..offset + len].copy_from_slice(&bytes);
Ok(())
}
pub fn do_finalize(keyed_accounts: &[KeyedAccount]) -> Result<(), InstructionError> {
let mut keyed_accounts_iter = keyed_accounts.iter();
let finalized = next_keyed_account(&mut keyed_accounts_iter)?;
if finalized.signer_key().is_none() {
debug!("Error: account to finalize did not sign the transaction");
return Err(InstructionError::MissingRequiredSignature);
}
match finalized.state()? {
LibraAccountState::CompiledScript(string) => {
let script: Script = serde_json::from_str(&string).map_err(map_json_error)?;
let compiled_script =
CompiledScript::deserialize(&script.code()).map_err(map_err_vm_status)?;
let verified_script = match VerifiedScript::new(compiled_script) {
Ok(script) => script,
Err((_, errors)) => {
if errors.is_empty() {
return Err(MoveError::InvalidScript.into());
} else {
return Err(map_err_vm_status(errors[0].clone()));
}
}
};
let mut script_bytes = vec![];
verified_script
.as_inner()
.serialize(&mut script_bytes)
.map_err(map_failure_error)?;
Self::serialize_and_enforce_length(
&LibraAccountState::VerifiedScript { script_bytes },
&mut finalized.try_account_ref_mut()?.data,
)?;
info!("Finalize script: {:?}", finalized.unsigned_key());
}
LibraAccountState::CompiledModule(string) => {
let module: Module = serde_json::from_str(&string).map_err(map_json_error)?;
let compiled_module =
CompiledModule::deserialize(&module.code()).map_err(map_err_vm_status)?;
let verified_module =
VerifiedModule::new(compiled_module).map_err(map_vm_verification_error)?;
let mut data_store = DataStore::default();
data_store
.add_module(&verified_module)
.map_err(map_failure_error)?;
let mut write_sets = data_store
.into_write_sets()
.map_err(|_| MoveError::InvalidDataStore)?;
let write_set = write_sets
.remove(&pubkey_to_address(finalized.unsigned_key()))
.ok_or_else(Self::missing_account)?;
Self::serialize_and_enforce_length(
&LibraAccountState::PublishedModule(write_set),
&mut finalized.try_account_ref_mut()?.data,
)?;
if !write_sets.is_empty() {
debug!("Error: Missing keyed accounts");
return Err(InstructionError::NotEnoughAccountKeys);
}
info!("Finalize module: {:?}", finalized.unsigned_key());
}
_ => {
debug!("Error: Account to finalize does not contain compiled data");
return Err(InstructionError::InvalidArgument);
}
};
finalized.try_account_ref_mut()?.executable = true;
Ok(())
}
pub fn do_create_genesis(
keyed_accounts: &[KeyedAccount],
amount: u64,
) -> Result<(), InstructionError> {
let mut keyed_accounts_iter = keyed_accounts.iter();
let genesis = next_keyed_account(&mut keyed_accounts_iter)?;
if genesis.owner()? != id() {
debug!("Error: Move genesis account not owned by Move loader");
return Err(InstructionError::InvalidArgument);
}
match genesis.state()? {
LibraAccountState::Unallocated => Self::serialize_and_enforce_length(
&LibraAccountState::create_genesis(amount)?,
&mut genesis.try_account_ref_mut()?.data,
),
_ => {
debug!("Error: Must provide an unallocated account");
Err(InstructionError::InvalidArgument)
}
}
}
pub fn do_invoke_main(
keyed_accounts: &[KeyedAccount],
sender_address: AccountAddress,
function_name: String,
args: Vec<TransactionArgument>,
) -> Result<(), InstructionError> {
let mut keyed_accounts_iter = keyed_accounts.iter();
let script = next_keyed_account(&mut keyed_accounts_iter)?;
trace!(
"Run script {:?} with entrypoint {:?}",
script.unsigned_key(),
function_name
);
if script.owner()? != id() {
debug!("Error: Move script account not owned by Move loader");
return Err(InstructionError::InvalidArgument);
}
if !script.executable()? {
debug!("Error: Move script account not executable");
return Err(InstructionError::AccountNotExecutable);
}
let data_accounts = keyed_accounts_iter.as_slice();
let mut data_store = Self::keyed_accounts_to_data_store(&data_accounts)?;
let verified_script = Self::deserialize_verified_script(&script.try_account_ref()?.data)?;
let output = Self::execute(
sender_address,
&function_name,
args,
verified_script,
&data_store,
)?;
for event in output.events() {
trace!("Event: {:?}", event);
}
data_store.apply_write_set(&output.write_set());
Self::data_store_to_keyed_accounts(data_store, data_accounts)
}
pub fn process_instruction(
_program_id: &Pubkey,
keyed_accounts: &[KeyedAccount],
instruction_data: &[u8],
_invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
solana_logger::setup();
if is_executable(keyed_accounts)? {
match limited_deserialize(&instruction_data)? {
Executable::RunScript {
sender_address,
function_name,
args,
} => Self::do_invoke_main(keyed_accounts, sender_address, function_name, args),
}
} else {
match limited_deserialize(instruction_data)? {
MoveLoaderInstruction::Write { offset, bytes } => {
Self::do_write(keyed_accounts, offset, &bytes)
}
MoveLoaderInstruction::Finalize => Self::do_finalize(keyed_accounts),
MoveLoaderInstruction::CreateGenesis(amount) => {
Self::do_create_genesis(keyed_accounts, amount)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use solana_sdk::account::Account;
use std::cell::RefCell;
const BIG_ENOUGH: usize = 10_000;
#[test]
fn test_account_size() {
let mut data =
vec![0_u8; bincode::serialized_size(&LibraAccountState::Unallocated).unwrap() as usize];
let len = data.len();
assert_eq!(
MoveProcessor::serialize_and_enforce_length(&LibraAccountState::Unallocated, &mut data),
Ok(())
);
assert_eq!(len, data.len());
data.resize(6000, 0);
let len = data.len();
assert_eq!(
MoveProcessor::serialize_and_enforce_length(&LibraAccountState::Unallocated, &mut data),
Ok(())
);
assert_eq!(len, data.len());
data.resize(1, 0);
assert_eq!(
MoveProcessor::serialize_and_enforce_length(&LibraAccountState::Unallocated, &mut data),
Err(InstructionError::AccountDataTooSmall)
);
}
#[test]
fn test_finalize() {
solana_logger::setup();
let code = "main() { return; }";
let sender_address = AccountAddress::default();
let script = LibraAccount::create_script(&sender_address, code, vec![]);
let keyed_accounts = vec![KeyedAccount::new(&script.key, true, &script.account)];
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
let _ = MoveProcessor::deserialize_verified_script(&script.account.borrow().data).unwrap();
}
#[test]
fn test_create_genesis_account() {
solana_logger::setup();
let amount = 10_000_000;
let unallocated = LibraAccount::create_unallocated(BIG_ENOUGH);
let keyed_accounts = vec![KeyedAccount::new(
&unallocated.key,
false,
&unallocated.account,
)];
MoveProcessor::do_create_genesis(&keyed_accounts, amount).unwrap();
assert_eq!(
bincode::deserialize::<LibraAccountState>(
&LibraAccount::create_genesis(amount).account.borrow().data
)
.unwrap(),
bincode::deserialize::<LibraAccountState>(&keyed_accounts[0].account.borrow().data)
.unwrap()
);
}
#[test]
fn test_invoke_script() {
solana_logger::setup();
let code = "main() { return; }";
let sender_address = AccountAddress::default();
let script = LibraAccount::create_script(&sender_address, code, vec![]);
let genesis = LibraAccount::create_genesis(1_000_000_000);
let keyed_accounts = vec![KeyedAccount::new(&script.key, true, &script.account)];
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
let keyed_accounts = vec![
KeyedAccount::new(&script.key, true, &script.account),
KeyedAccount::new(&genesis.key, false, &genesis.account),
];
MoveProcessor::do_invoke_main(&keyed_accounts, sender_address, "main".to_string(), vec![])
.unwrap();
}
#[test]
fn test_publish_module() {
solana_logger::setup();
let code = "
module M {
universal_truth(): u64 {
return 42;
}
}
";
let module = LibraAccount::create_module(code, vec![]);
let keyed_accounts = vec![KeyedAccount::new(&module.key, true, &module.account)];
keyed_accounts[0]
.account
.borrow_mut()
.data
.resize(BIG_ENOUGH, 0);
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
}
#[test]
fn test_invoke_endless_loop() {
solana_logger::setup();
let code = "
main() {
loop {}
return;
}
";
let sender_address = AccountAddress::default();
let script = LibraAccount::create_script(&sender_address, code, vec![]);
let genesis = LibraAccount::create_genesis(1_000_000_000);
let keyed_accounts = vec![KeyedAccount::new(&script.key, true, &script.account)];
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
let keyed_accounts = vec![
KeyedAccount::new(&script.key, true, &script.account),
KeyedAccount::new(&genesis.key, false, &genesis.account),
];
assert_eq!(
MoveProcessor::do_invoke_main(
&keyed_accounts,
sender_address,
"main".to_string(),
vec![],
),
Err(InstructionError::Custom(4002))
);
}
#[test]
fn test_invoke_mint_to_address() {
solana_logger::setup();
let mut data_store = DataStore::default();
let amount = 42;
let accounts = mint_coins(amount).unwrap();
let mut accounts_iter = accounts.iter();
let _script = next_libra_account(&mut accounts_iter).unwrap();
let genesis = next_libra_account(&mut accounts_iter).unwrap();
let payee = next_libra_account(&mut accounts_iter).unwrap();
match bincode::deserialize(&payee.account.borrow().data).unwrap() {
LibraAccountState::User(owner, write_set) => {
if owner != genesis.key {
panic!();
}
data_store.apply_write_set(&write_set)
}
_ => panic!("Invalid account state"),
}
let payee_resource = data_store.read_account_resource(&payee.address).unwrap();
assert_eq!(amount, payee_resource.balance());
assert_eq!(0, payee_resource.sequence_number());
}
#[test]
fn test_invoke_pay_from_sender() {
solana_logger::setup();
let amount_to_mint = 42;
let accounts = mint_coins(amount_to_mint).unwrap();
let mut accounts_iter = accounts.iter();
let _script = next_libra_account(&mut accounts_iter).unwrap();
let genesis = next_libra_account(&mut accounts_iter).unwrap();
let sender = next_libra_account(&mut accounts_iter).unwrap();
let code = "
import 0x0.LibraAccount;
import 0x0.LibraCoin;
main(payee: address, amount: u64) {
LibraAccount.pay_from_sender(move(payee), move(amount));
return;
}
";
let script = LibraAccount::create_script(&genesis.address, code, vec![]);
let payee = LibraAccount::create_unallocated(BIG_ENOUGH);
let keyed_accounts = vec![KeyedAccount::new(&script.key, true, &script.account)];
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
let keyed_accounts = vec![
KeyedAccount::new(&script.key, true, &script.account),
KeyedAccount::new(&genesis.key, false, &genesis.account),
KeyedAccount::new(&sender.key, false, &sender.account),
KeyedAccount::new(&payee.key, false, &payee.account),
];
let amount = 2;
MoveProcessor::do_invoke_main(
&keyed_accounts,
sender.address.clone(),
"main".to_string(),
vec![
TransactionArgument::Address(payee.address.clone()),
TransactionArgument::U64(amount),
],
)
.unwrap();
let data_store = MoveProcessor::keyed_accounts_to_data_store(&keyed_accounts[1..]).unwrap();
let sender_resource = data_store.read_account_resource(&sender.address).unwrap();
let payee_resource = data_store.read_account_resource(&payee.address).unwrap();
assert_eq!(amount_to_mint - amount, sender_resource.balance());
assert_eq!(0, sender_resource.sequence_number());
assert_eq!(amount, payee_resource.balance());
assert_eq!(0, payee_resource.sequence_number());
}
#[test]
fn test_invoke_published_module() {
solana_logger::setup();
let universal_truth = 42;
// First publish the module
let code = format!(
"
module M {{
public universal_truth(): u64 {{
return {};
}}
}}
",
universal_truth
);
let module = LibraAccount::create_module(&code, vec![]);
let keyed_accounts = vec![KeyedAccount::new(&module.key, true, &module.account)];
keyed_accounts[0]
.account
.borrow_mut()
.data
.resize(BIG_ENOUGH, 0);
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
// Next invoke the published module
let amount_to_mint = 84;
let accounts = mint_coins(amount_to_mint).unwrap();
let mut accounts_iter = accounts.iter();
let _script = next_libra_account(&mut accounts_iter).unwrap();
let genesis = next_libra_account(&mut accounts_iter).unwrap();
let sender = next_libra_account(&mut accounts_iter).unwrap();
let code = format!(
"
import 0x0.LibraAccount;
import 0x0.LibraCoin;
import 0x{}.M;
main(payee: address) {{
let amount: u64;
amount = M.universal_truth();
LibraAccount.pay_from_sender(move(payee), move(amount));
return;
}}
",
module.address
);
let script = LibraAccount::create_script(
&genesis.address,
&code,
vec![&module.account.borrow().data],
);
let payee = LibraAccount::create_unallocated(BIG_ENOUGH);
let keyed_accounts = vec![KeyedAccount::new(&script.key, true, &script.account)];
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
let keyed_accounts = vec![
KeyedAccount::new(&script.key, true, &script.account),
KeyedAccount::new(&genesis.key, false, &genesis.account),
KeyedAccount::new(&sender.key, false, &sender.account),
KeyedAccount::new(&module.key, false, &module.account),
KeyedAccount::new(&payee.key, false, &payee.account),
];
MoveProcessor::do_invoke_main(
&keyed_accounts,
sender.address.clone(),
"main".to_string(),
vec![TransactionArgument::Address(payee.address.clone())],
)
.unwrap();
let data_store = MoveProcessor::keyed_accounts_to_data_store(&keyed_accounts[1..]).unwrap();
let sender_resource = data_store.read_account_resource(&sender.address).unwrap();
let payee_resource = data_store.read_account_resource(&payee.address).unwrap();
assert_eq!(amount_to_mint - universal_truth, sender_resource.balance());
assert_eq!(0, sender_resource.sequence_number());
assert_eq!(universal_truth, payee_resource.balance());
assert_eq!(0, payee_resource.sequence_number());
}
// Helpers
fn mint_coins(amount: u64) -> Result<Vec<LibraAccount>, InstructionError> {
let code = "
import 0x0.LibraAccount;
import 0x0.LibraCoin;
main(payee: address, amount: u64) {
LibraAccount.mint_to_address(move(payee), move(amount));
return;
}
";
let genesis = LibraAccount::create_genesis(1_000_000_000);
let script = LibraAccount::create_script(&genesis.address.clone(), code, vec![]);
let payee = LibraAccount::create_unallocated(BIG_ENOUGH);
let keyed_accounts = vec![KeyedAccount::new(&script.key, true, &script.account)];
MoveProcessor::do_finalize(&keyed_accounts).unwrap();
let keyed_accounts = vec![
KeyedAccount::new(&script.key, true, &script.account),
KeyedAccount::new(&genesis.key, false, &genesis.account),
KeyedAccount::new(&payee.key, false, &payee.account),
];
MoveProcessor::do_invoke_main(
&keyed_accounts,
account_config::association_address(),
"main".to_string(),
vec![
TransactionArgument::Address(pubkey_to_address(&payee.key)),
TransactionArgument::U64(amount),
],
)
.unwrap();
Ok(vec![
LibraAccount::new(script.key, script.account.into_inner()),
LibraAccount::new(genesis.key, genesis.account.into_inner()),
LibraAccount::new(payee.key, payee.account.into_inner()),
])
}
#[derive(Eq, PartialEq, Debug, Default)]
struct LibraAccount {
pub key: Pubkey,
pub address: AccountAddress,
pub account: RefCell<Account>,
}
pub fn next_libra_account<I: Iterator>(iter: &mut I) -> Result<I::Item, InstructionError> {
iter.next().ok_or(InstructionError::NotEnoughAccountKeys)
}
impl LibraAccount {
pub fn new(key: Pubkey, account: Account) -> Self {
let address = pubkey_to_address(&key);
Self {
key,
address,
account: RefCell::new(account),
}
}
pub fn create_unallocated(size: usize) -> Self {
let key = Pubkey::new_rand();
let mut account = Account {
lamports: 1,
data: bincode::serialize(&LibraAccountState::create_unallocated()).unwrap(),
owner: id(),
..Account::default()
};
if account.data.len() < size {
account.data.resize(size, 0);
}
Self::new(key, account)
}
pub fn create_genesis(amount: u64) -> Self {
let account = Account {
lamports: 1,
owner: id(),
..Account::default()
};
let genesis = Self::new(Pubkey::new_rand(), account);
let pre_data = LibraAccountState::create_genesis(amount).unwrap();
let _hi = "hello";
genesis.account.borrow_mut().data = bincode::serialize(&pre_data).unwrap();
genesis
}
pub fn create_script(
sender_address: &AccountAddress,
code: &str,
deps: Vec<&Vec<u8>>,
) -> Self {
let script = Self::create_unallocated(0);
script.account.borrow_mut().data = bincode::serialize(
&LibraAccountState::create_script(sender_address, code, deps),
)
.unwrap();
script
}
pub fn create_module(code: &str, deps: Vec<&Vec<u8>>) -> Self {
let module = Self::create_unallocated(0);
module.account.borrow_mut().data = bincode::serialize(
&LibraAccountState::create_module(&module.address, code, deps),
)
.unwrap();
module
}
}
}

View File

@ -11,23 +11,19 @@ usage() {
echo "Error: $*"
fi
cat <<EOF
usage: $0 [+<cargo version>] [--use-move] [--debug] <install directory>
usage: $0 [+<cargo version>] [--debug] <install directory>
EOF
exit $exitcode
}
maybeRustVersion=
useMove=false
installDir=
buildVariant=release
maybeReleaseFlag=--release
while [[ -n $1 ]]; do
if [[ ${1:0:1} = - ]]; then
if [[ $1 = --use-move ]]; then
useMove=true
shift
elif [[ $1 = --debug ]]; then
if [[ $1 = --debug ]]; then
maybeReleaseFlag=
buildVariant=debug
shift
@ -75,13 +71,6 @@ else
set -x
# shellcheck disable=SC2086 # Don't want to double quote $rust_version
$cargo $maybeRustVersion build $maybeReleaseFlag
if $useMove; then
moveLoaderDir=programs/move_loader
# shellcheck disable=SC2086 # Don't want to double quote $rust_version
$cargo $maybeRustVersion build $maybeReleaseFlag --manifest-path "$moveLoaderDir/Cargo.toml"
cp -fv $moveLoaderDir/target/$buildVariant/libsolana_move_loader_program.* "$installDir/bin/deps"
fi
)

View File

@ -25,7 +25,6 @@ pub mod inflation;
pub mod instruction;
pub mod loader_instruction;
pub mod message;
pub mod move_loader;
pub mod native_loader;
pub mod native_token;
pub mod nonce;

View File

@ -1,5 +0,0 @@
crate::declare_id!("MoveLdr111111111111111111111111111111111111");
pub fn solana_move_loader_program() -> (String, crate::pubkey::Pubkey) {
("solana_move_loader_program".to_string(), id())
}