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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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())
}