Sysvar example (#399)
This commit is contained in:
parent
e9cc9f8379
commit
bcaf7a8f6c
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "clock-sysvar-program"
|
||||||
|
version = "2.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
solana-program = { path = "../../../../sdk/program", version = "=2.0.0" }
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[workspace]
|
Binary file not shown.
|
@ -0,0 +1,18 @@
|
||||||
|
use solana_program::{
|
||||||
|
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey,
|
||||||
|
sysvar::{clock::Clock, Sysvar}, program::set_return_data
|
||||||
|
};
|
||||||
|
|
||||||
|
entrypoint!(process_instruction);
|
||||||
|
|
||||||
|
fn process_instruction(
|
||||||
|
_program_id: &Pubkey,
|
||||||
|
_accounts: &[AccountInfo],
|
||||||
|
_instruction_data: &[u8],
|
||||||
|
) -> ProgramResult {
|
||||||
|
|
||||||
|
let time_now = Clock::get().unwrap().unix_timestamp;
|
||||||
|
let return_data = time_now.to_be_bytes();
|
||||||
|
set_return_data(&return_data);
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -3,7 +3,8 @@
|
||||||
use {
|
use {
|
||||||
crate::mock_bank::MockBankCallback,
|
crate::mock_bank::MockBankCallback,
|
||||||
solana_bpf_loader_program::syscalls::{
|
solana_bpf_loader_program::syscalls::{
|
||||||
SyscallAbort, SyscallInvokeSignedRust, SyscallLog, SyscallMemcpy, SyscallMemset,
|
SyscallAbort, SyscallGetClockSysvar, SyscallInvokeSignedRust, SyscallLog, SyscallMemcpy,
|
||||||
|
SyscallMemset, SyscallSetReturnData,
|
||||||
},
|
},
|
||||||
solana_program_runtime::{
|
solana_program_runtime::{
|
||||||
compute_budget::ComputeBudget,
|
compute_budget::ComputeBudget,
|
||||||
|
@ -21,7 +22,7 @@ use {
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account::{AccountSharedData, ReadableAccount, WritableAccount},
|
account::{AccountSharedData, ReadableAccount, WritableAccount},
|
||||||
bpf_loader,
|
bpf_loader,
|
||||||
clock::{Epoch, Slot},
|
clock::{Clock, Epoch, Slot, UnixTimestamp},
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
fee::FeeStructure,
|
fee::FeeStructure,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
@ -30,12 +31,15 @@ use {
|
||||||
native_loader,
|
native_loader,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::Signature,
|
signature::Signature,
|
||||||
|
sysvar::SysvarId,
|
||||||
transaction::{SanitizedTransaction, Transaction},
|
transaction::{SanitizedTransaction, Transaction},
|
||||||
},
|
},
|
||||||
solana_svm::{
|
solana_svm::{
|
||||||
account_loader::TransactionCheckResult,
|
account_loader::TransactionCheckResult,
|
||||||
transaction_error_metrics::TransactionErrorMetrics,
|
transaction_error_metrics::TransactionErrorMetrics,
|
||||||
transaction_processor::{ExecutionRecordingConfig, TransactionBatchProcessor},
|
transaction_processor::{
|
||||||
|
ExecutionRecordingConfig, TransactionBatchProcessor, TransactionProcessingCallback,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
|
@ -43,6 +47,7 @@ use {
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::Read,
|
io::Read,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,6 +120,14 @@ fn create_custom_environment<'a>() -> BuiltinProgram<InvokeContext<'a>> {
|
||||||
.register_function_hashed(*b"sol_invoke_signed_rust", SyscallInvokeSignedRust::vm)
|
.register_function_hashed(*b"sol_invoke_signed_rust", SyscallInvokeSignedRust::vm)
|
||||||
.expect("Registration failed");
|
.expect("Registration failed");
|
||||||
|
|
||||||
|
function_registry
|
||||||
|
.register_function_hashed(*b"sol_set_return_data", SyscallSetReturnData::vm)
|
||||||
|
.expect("Registration failed");
|
||||||
|
|
||||||
|
function_registry
|
||||||
|
.register_function_hashed(*b"sol_get_clock_sysvar", SyscallGetClockSysvar::vm)
|
||||||
|
.expect("Registration failed");
|
||||||
|
|
||||||
BuiltinProgram::new_loader(vm_config, function_registry)
|
BuiltinProgram::new_loader(vm_config, function_registry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +185,25 @@ fn create_executable_environment(
|
||||||
|
|
||||||
program_cache.fork_graph = Some(Arc::new(RwLock::new(MockForkGraph {})));
|
program_cache.fork_graph = Some(Arc::new(RwLock::new(MockForkGraph {})));
|
||||||
|
|
||||||
|
// We must fill in the sysvar cache entries
|
||||||
|
let time_now = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("Time went backwards")
|
||||||
|
.as_secs() as i64;
|
||||||
|
let clock = Clock {
|
||||||
|
slot: DEPLOYMENT_SLOT,
|
||||||
|
epoch_start_timestamp: time_now.saturating_sub(10) as UnixTimestamp,
|
||||||
|
epoch: DEPLOYMENT_EPOCH,
|
||||||
|
leader_schedule_epoch: DEPLOYMENT_EPOCH,
|
||||||
|
unix_timestamp: time_now as UnixTimestamp,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut account_data = AccountSharedData::default();
|
||||||
|
account_data.set_data(bincode::serialize(&clock).unwrap());
|
||||||
|
mock_bank
|
||||||
|
.account_shared_data
|
||||||
|
.insert(Clock::id(), account_data);
|
||||||
|
|
||||||
// Inform SVM of the registered builins
|
// Inform SVM of the registered builins
|
||||||
let registered_built_ins = vec![bpf_loader::id(), solana_system_program::id()];
|
let registered_built_ins = vec![bpf_loader::id(), solana_system_program::id()];
|
||||||
(program_cache, registered_built_ins)
|
(program_cache, registered_built_ins)
|
||||||
|
@ -319,8 +351,53 @@ fn prepare_transactions(
|
||||||
|
|
||||||
// The program account is set in `create_executable_environment`
|
// The program account is set in `create_executable_environment`
|
||||||
|
|
||||||
|
// A program that utilizes a Sysvar
|
||||||
|
let program_account = Pubkey::new_unique();
|
||||||
|
let fee_payer = Pubkey::new_unique();
|
||||||
|
let message = Message {
|
||||||
|
account_keys: vec![fee_payer, program_account],
|
||||||
|
header: MessageHeader {
|
||||||
|
num_required_signatures: 1,
|
||||||
|
num_readonly_signed_accounts: 0,
|
||||||
|
num_readonly_unsigned_accounts: 0,
|
||||||
|
},
|
||||||
|
instructions: vec![CompiledInstruction {
|
||||||
|
program_id_index: 1,
|
||||||
|
accounts: vec![],
|
||||||
|
data: vec![],
|
||||||
|
}],
|
||||||
|
recent_blockhash: Hash::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let transaction = Transaction {
|
||||||
|
signatures: vec![Signature::new_unique()],
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
let sanitized_transaction =
|
||||||
|
SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap();
|
||||||
|
all_transactions.push(sanitized_transaction);
|
||||||
|
transaction_checks.push((Ok(()), None, Some(20)));
|
||||||
|
|
||||||
|
let mut account_data = AccountSharedData::default();
|
||||||
|
account_data.set_lamports(80000);
|
||||||
|
mock_bank
|
||||||
|
.account_shared_data
|
||||||
|
.insert(fee_payer, account_data);
|
||||||
|
|
||||||
|
let buffer = load_program("clock-sysvar".to_string());
|
||||||
|
|
||||||
|
// The program account must have funds and hold the executable binary
|
||||||
|
let mut account_data = AccountSharedData::default();
|
||||||
|
// The executable account owner must be one of the loaders.
|
||||||
|
account_data.set_owner(bpf_loader::id());
|
||||||
|
account_data.set_data(buffer);
|
||||||
|
account_data.set_executable(true);
|
||||||
|
account_data.set_lamports(25);
|
||||||
|
mock_bank
|
||||||
|
.account_shared_data
|
||||||
|
.insert(program_account, account_data);
|
||||||
|
|
||||||
// TODO: Include these examples as well:
|
// TODO: Include these examples as well:
|
||||||
// An example with a sysvar
|
|
||||||
// A transaction that fails
|
// A transaction that fails
|
||||||
// A transaction whose verification has already failed
|
// A transaction whose verification has already failed
|
||||||
|
|
||||||
|
@ -342,10 +419,21 @@ fn svm_integration() {
|
||||||
program_cache.clone(),
|
program_cache.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// The sysvars must be put in the cache
|
||||||
|
batch_processor
|
||||||
|
.sysvar_cache
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.fill_missing_entries(|pubkey, callback| {
|
||||||
|
if let Some(account) = mock_bank.get_account_shared_data(pubkey) {
|
||||||
|
callback(account.data());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let mut error_counter = TransactionErrorMetrics::default();
|
let mut error_counter = TransactionErrorMetrics::default();
|
||||||
let recording_config = ExecutionRecordingConfig {
|
let recording_config = ExecutionRecordingConfig {
|
||||||
enable_log_recording: true,
|
enable_log_recording: true,
|
||||||
enable_return_data_recording: false,
|
enable_return_data_recording: true,
|
||||||
enable_cpi_recording: false,
|
enable_cpi_recording: false,
|
||||||
};
|
};
|
||||||
let mut timings = ExecuteTimings::default();
|
let mut timings = ExecuteTimings::default();
|
||||||
|
@ -363,7 +451,7 @@ fn svm_integration() {
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(result.execution_results.len(), 2);
|
assert_eq!(result.execution_results.len(), 3);
|
||||||
assert!(result.execution_results[0]
|
assert!(result.execution_results[0]
|
||||||
.details()
|
.details()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -394,4 +482,15 @@ fn svm_integration() {
|
||||||
.find(|key| key.0 == recipient_key)
|
.find(|key| key.0 == recipient_key)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(recipient_data.1.lamports(), 900010);
|
assert_eq!(recipient_data.1.lamports(), 900010);
|
||||||
|
|
||||||
|
let return_data = result.execution_results[2]
|
||||||
|
.details()
|
||||||
|
.unwrap()
|
||||||
|
.return_data
|
||||||
|
.as_ref()
|
||||||
|
.unwrap();
|
||||||
|
let time = i64::from_be_bytes(return_data.data[0..8].try_into().unwrap());
|
||||||
|
let clock_data = mock_bank.get_account_shared_data(&Clock::id()).unwrap();
|
||||||
|
let clock_info: Clock = bincode::deserialize(clock_data.data()).unwrap();
|
||||||
|
assert_eq!(clock_info.unix_timestamp, time);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue