CLI Nonce account access dereplicode (#8743)
* Spruce up CliNonceError * Add nonce account access helpers * Use helpers throughout
This commit is contained in:
parent
595c96b262
commit
261732f140
|
@ -3783,6 +3783,7 @@ dependencies = [
|
||||||
"solana-vote-program 1.1.0",
|
"solana-vote-program 1.1.0",
|
||||||
"solana-vote-signer 1.1.0",
|
"solana-vote-signer 1.1.0",
|
||||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"titlecase 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"titlecase 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
|
@ -42,6 +42,7 @@ solana-storage-program = { path = "../programs/storage", version = "1.1.0" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.1.0" }
|
solana-vote-program = { path = "../programs/vote", version = "1.1.0" }
|
||||||
solana-vote-signer = { path = "../vote-signer", version = "1.1.0" }
|
solana-vote-signer = { path = "../vote-signer", version = "1.1.0" }
|
||||||
titlecase = "1.1.0"
|
titlecase = "1.1.0"
|
||||||
|
thiserror = "1.0.11"
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -409,7 +409,7 @@ pub struct CliCommandInfo {
|
||||||
pub signers: CliSigners,
|
pub signers: CliSigners,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum CliError {
|
pub enum CliError {
|
||||||
BadParameter(String),
|
BadParameter(String),
|
||||||
CommandNotRecognized(String),
|
CommandNotRecognized(String),
|
||||||
|
@ -443,6 +443,17 @@ impl From<Box<dyn error::Error>> for CliError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<CliNonceError> for CliError {
|
||||||
|
fn from(error: CliNonceError) -> Self {
|
||||||
|
match error {
|
||||||
|
CliNonceError::Client(client_error) => {
|
||||||
|
Self::RpcRequestError(format!("{:?}", client_error))
|
||||||
|
}
|
||||||
|
_ => Self::InvalidNonce(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum SettingType {
|
pub enum SettingType {
|
||||||
Explicit,
|
Explicit,
|
||||||
Computed,
|
Computed,
|
||||||
|
|
213
cli/src/nonce.rs
213
cli/src/nonce.rs
|
@ -14,7 +14,11 @@ use solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::Message,
|
message::Message,
|
||||||
nonce::{self, state::Versions, State},
|
nonce::{
|
||||||
|
self,
|
||||||
|
state::{Data, Versions},
|
||||||
|
State,
|
||||||
|
},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
system_instruction::{
|
system_instruction::{
|
||||||
advance_nonce_account, authorize_nonce_account, create_address_with_seed,
|
advance_nonce_account, authorize_nonce_account, create_address_with_seed,
|
||||||
|
@ -25,14 +29,24 @@ use solana_sdk::{
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Error, PartialEq)]
|
||||||
pub enum CliNonceError {
|
pub enum CliNonceError {
|
||||||
|
#[error("invalid account owner")]
|
||||||
InvalidAccountOwner,
|
InvalidAccountOwner,
|
||||||
|
#[error("invalid account data")]
|
||||||
InvalidAccountData,
|
InvalidAccountData,
|
||||||
|
#[error("unexpected account data size")]
|
||||||
|
UnexpectedDataSize,
|
||||||
|
#[error("query hash does not match stored hash")]
|
||||||
InvalidHash,
|
InvalidHash,
|
||||||
|
#[error("query authority does not match account authority")]
|
||||||
InvalidAuthority,
|
InvalidAuthority,
|
||||||
InvalidState,
|
#[error("invalid state for requested operation")]
|
||||||
|
InvalidStateForOperation,
|
||||||
|
#[error("client error: {0}")]
|
||||||
|
Client(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const NONCE_ARG: ArgConstant<'static> = ArgConstant {
|
pub const NONCE_ARG: ArgConstant<'static> = ArgConstant {
|
||||||
|
@ -218,6 +232,48 @@ impl NonceSubCommands for App<'_, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_account(
|
||||||
|
rpc_client: &RpcClient,
|
||||||
|
nonce_pubkey: &Pubkey,
|
||||||
|
) -> Result<Account, CliNonceError> {
|
||||||
|
rpc_client
|
||||||
|
.get_account(nonce_pubkey)
|
||||||
|
.map_err(|e| CliNonceError::Client(format!("{:?}", e)))
|
||||||
|
.and_then(|a| match account_identity_ok(&a) {
|
||||||
|
Ok(()) => Ok(a),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn account_identity_ok(account: &Account) -> Result<(), CliNonceError> {
|
||||||
|
if account.owner != system_program::id() {
|
||||||
|
Err(CliNonceError::InvalidAccountOwner)
|
||||||
|
} else if account.data.is_empty() {
|
||||||
|
Err(CliNonceError::UnexpectedDataSize)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state_from_account(account: &Account) -> Result<State, CliNonceError> {
|
||||||
|
account_identity_ok(account)?;
|
||||||
|
StateMut::<Versions>::state(account)
|
||||||
|
.map_err(|_| CliNonceError::InvalidAccountData)
|
||||||
|
.map(|v| v.convert_to_current())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data_from_account(account: &Account) -> Result<Data, CliNonceError> {
|
||||||
|
account_identity_ok(account)?;
|
||||||
|
state_from_account(account).and_then(|ref s| data_from_state(s).map(|d| d.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data_from_state(state: &State) -> Result<&Data, CliNonceError> {
|
||||||
|
match state {
|
||||||
|
State::Uninitialized => Err(CliNonceError::InvalidStateForOperation),
|
||||||
|
State::Initialized(data) => Ok(data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_authorize_nonce_account(
|
pub fn parse_authorize_nonce_account(
|
||||||
matches: &ArgMatches<'_>,
|
matches: &ArgMatches<'_>,
|
||||||
default_signer_path: &str,
|
default_signer_path: &str,
|
||||||
|
@ -359,24 +415,18 @@ pub fn check_nonce_account(
|
||||||
nonce_account: &Account,
|
nonce_account: &Account,
|
||||||
nonce_authority: &Pubkey,
|
nonce_authority: &Pubkey,
|
||||||
nonce_hash: &Hash,
|
nonce_hash: &Hash,
|
||||||
) -> Result<(), Box<CliError>> {
|
) -> Result<(), CliError> {
|
||||||
if nonce_account.owner != system_program::ID {
|
match state_from_account(nonce_account)? {
|
||||||
return Err(CliError::InvalidNonce(CliNonceError::InvalidAccountOwner).into());
|
|
||||||
}
|
|
||||||
let nonce_state = StateMut::<Versions>::state(nonce_account)
|
|
||||||
.map(|v| v.convert_to_current())
|
|
||||||
.map_err(|_| Box::new(CliError::InvalidNonce(CliNonceError::InvalidAccountData)))?;
|
|
||||||
match nonce_state {
|
|
||||||
State::Initialized(ref data) => {
|
State::Initialized(ref data) => {
|
||||||
if &data.blockhash != nonce_hash {
|
if &data.blockhash != nonce_hash {
|
||||||
Err(CliError::InvalidNonce(CliNonceError::InvalidHash).into())
|
Err(CliNonceError::InvalidHash.into())
|
||||||
} else if nonce_authority != &data.authority {
|
} else if nonce_authority != &data.authority {
|
||||||
Err(CliError::InvalidNonce(CliNonceError::InvalidAuthority).into())
|
Err(CliNonceError::InvalidAuthority.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
State::Uninitialized => Err(CliError::InvalidNonce(CliNonceError::InvalidState).into()),
|
State::Uninitialized => Err(CliNonceError::InvalidStateForOperation.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,10 +475,8 @@ pub fn process_create_nonce_account(
|
||||||
(&nonce_account_address, "nonce_account".to_string()),
|
(&nonce_account_address, "nonce_account".to_string()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Ok(nonce_account) = rpc_client.get_account(&nonce_account_address) {
|
if let Ok(nonce_account) = get_account(rpc_client, &nonce_account_address) {
|
||||||
let err_msg = if nonce_account.owner == system_program::id()
|
let err_msg = if state_from_account(&nonce_account).is_ok() {
|
||||||
&& StateMut::<Versions>::state(&nonce_account).is_ok()
|
|
||||||
{
|
|
||||||
format!("Nonce account {} already exists", nonce_account_address)
|
format!("Nonce account {} already exists", nonce_account_address)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
|
@ -485,23 +533,9 @@ pub fn process_create_nonce_account(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_get_nonce(rpc_client: &RpcClient, nonce_account_pubkey: &Pubkey) -> ProcessResult {
|
pub fn process_get_nonce(rpc_client: &RpcClient, nonce_account_pubkey: &Pubkey) -> ProcessResult {
|
||||||
let nonce_account = rpc_client.get_account(nonce_account_pubkey)?;
|
match get_account(rpc_client, nonce_account_pubkey).and_then(|ref a| state_from_account(a))? {
|
||||||
if nonce_account.owner != system_program::id() {
|
State::Uninitialized => Ok("Nonce account is uninitialized".to_string()),
|
||||||
return Err(CliError::RpcRequestError(format!(
|
State::Initialized(ref data) => Ok(format!("{:?}", data.blockhash)),
|
||||||
"{:?} is not a nonce account",
|
|
||||||
nonce_account_pubkey
|
|
||||||
))
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
let nonce_state = StateMut::<Versions>::state(&nonce_account).map(|v| v.convert_to_current());
|
|
||||||
match nonce_state {
|
|
||||||
Ok(State::Uninitialized) => Ok("Nonce account is uninitialized".to_string()),
|
|
||||||
Ok(State::Initialized(ref data)) => Ok(format!("{:?}", data.blockhash)),
|
|
||||||
Err(err) => Err(CliError::RpcRequestError(format!(
|
|
||||||
"Account data could not be deserialized to nonce state: {:?}",
|
|
||||||
err
|
|
||||||
))
|
|
||||||
.into()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,14 +579,7 @@ pub fn process_show_nonce_account(
|
||||||
nonce_account_pubkey: &Pubkey,
|
nonce_account_pubkey: &Pubkey,
|
||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let nonce_account = rpc_client.get_account(nonce_account_pubkey)?;
|
let nonce_account = get_account(rpc_client, nonce_account_pubkey)?;
|
||||||
if nonce_account.owner != system_program::id() {
|
|
||||||
return Err(CliError::RpcRequestError(format!(
|
|
||||||
"{:?} is not a nonce account",
|
|
||||||
nonce_account_pubkey
|
|
||||||
))
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
let print_account = |data: Option<&nonce::state::Data>| {
|
let print_account = |data: Option<&nonce::state::Data>| {
|
||||||
println!(
|
println!(
|
||||||
"Balance: {}",
|
"Balance: {}",
|
||||||
|
@ -583,15 +610,9 @@ pub fn process_show_nonce_account(
|
||||||
}
|
}
|
||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
};
|
};
|
||||||
let nonce_state = StateMut::<Versions>::state(&nonce_account).map(|v| v.convert_to_current());
|
match state_from_account(&nonce_account)? {
|
||||||
match nonce_state {
|
State::Uninitialized => print_account(None),
|
||||||
Ok(State::Uninitialized) => print_account(None),
|
State::Initialized(ref data) => print_account(Some(data)),
|
||||||
Ok(State::Initialized(ref data)) => print_account(Some(data)),
|
|
||||||
Err(err) => Err(CliError::RpcRequestError(format!(
|
|
||||||
"Account data could not be deserialized to nonce state: {:?}",
|
|
||||||
err
|
|
||||||
))
|
|
||||||
.into()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -920,17 +941,13 @@ mod tests {
|
||||||
let invalid_owner = Account::new_data(1, &data, &Pubkey::new(&[1u8; 32]));
|
let invalid_owner = Account::new_data(1, &data, &Pubkey::new(&[1u8; 32]));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
check_nonce_account(&invalid_owner.unwrap(), &nonce_pubkey, &blockhash),
|
check_nonce_account(&invalid_owner.unwrap(), &nonce_pubkey, &blockhash),
|
||||||
Err(Box::new(CliError::InvalidNonce(
|
Err(CliNonceError::InvalidAccountOwner.into()),
|
||||||
CliNonceError::InvalidAccountOwner
|
|
||||||
))),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let invalid_data = Account::new_data(1, &"invalid", &system_program::ID);
|
let invalid_data = Account::new_data(1, &"invalid", &system_program::ID);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
check_nonce_account(&invalid_data.unwrap(), &nonce_pubkey, &blockhash),
|
check_nonce_account(&invalid_data.unwrap(), &nonce_pubkey, &blockhash),
|
||||||
Err(Box::new(CliError::InvalidNonce(
|
Err(CliNonceError::InvalidAccountData.into()),
|
||||||
CliNonceError::InvalidAccountData
|
|
||||||
))),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
||||||
|
@ -941,7 +958,7 @@ mod tests {
|
||||||
let invalid_hash = Account::new_data(1, &data, &system_program::ID);
|
let invalid_hash = Account::new_data(1, &data, &system_program::ID);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
check_nonce_account(&invalid_hash.unwrap(), &nonce_pubkey, &blockhash),
|
check_nonce_account(&invalid_hash.unwrap(), &nonce_pubkey, &blockhash),
|
||||||
Err(Box::new(CliError::InvalidNonce(CliNonceError::InvalidHash))),
|
Err(CliNonceError::InvalidHash.into()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
||||||
|
@ -952,18 +969,84 @@ mod tests {
|
||||||
let invalid_authority = Account::new_data(1, &data, &system_program::ID);
|
let invalid_authority = Account::new_data(1, &data, &system_program::ID);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash),
|
check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash),
|
||||||
Err(Box::new(CliError::InvalidNonce(
|
Err(CliNonceError::InvalidAuthority.into()),
|
||||||
CliNonceError::InvalidAuthority
|
|
||||||
))),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let data = Versions::new_current(State::Uninitialized);
|
let data = Versions::new_current(State::Uninitialized);
|
||||||
let invalid_state = Account::new_data(1, &data, &system_program::ID);
|
let invalid_state = Account::new_data(1, &data, &system_program::ID);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash),
|
check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash),
|
||||||
Err(Box::new(CliError::InvalidNonce(
|
Err(CliNonceError::InvalidStateForOperation.into()),
|
||||||
CliNonceError::InvalidState
|
|
||||||
))),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_account_identity_ok() {
|
||||||
|
let nonce_account = nonce::create_account(1).into_inner();
|
||||||
|
assert_eq!(account_identity_ok(&nonce_account), Ok(()));
|
||||||
|
|
||||||
|
let system_account = Account::new(1, 0, &system_program::id());
|
||||||
|
assert_eq!(
|
||||||
|
account_identity_ok(&system_account),
|
||||||
|
Err(CliNonceError::UnexpectedDataSize),
|
||||||
|
);
|
||||||
|
|
||||||
|
let other_program = Pubkey::new(&[1u8; 32]);
|
||||||
|
let other_account_no_data = Account::new(1, 0, &other_program);
|
||||||
|
assert_eq!(
|
||||||
|
account_identity_ok(&other_account_no_data),
|
||||||
|
Err(CliNonceError::InvalidAccountOwner),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_state_from_account() {
|
||||||
|
let mut nonce_account = nonce::create_account(1).into_inner();
|
||||||
|
assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized));
|
||||||
|
|
||||||
|
let data = nonce::state::Data {
|
||||||
|
authority: Pubkey::new(&[1u8; 32]),
|
||||||
|
blockhash: Hash::new(&[42u8; 32]),
|
||||||
|
fee_calculator: FeeCalculator::new(42),
|
||||||
|
};
|
||||||
|
nonce_account
|
||||||
|
.set_state(&Versions::new_current(State::Initialized(data.clone())))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
state_from_account(&nonce_account),
|
||||||
|
Ok(State::Initialized(data))
|
||||||
|
);
|
||||||
|
|
||||||
|
let wrong_data_size_account = Account::new(1, 1, &system_program::id());
|
||||||
|
assert_eq!(
|
||||||
|
state_from_account(&wrong_data_size_account),
|
||||||
|
Err(CliNonceError::InvalidAccountData),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_data_from_helpers() {
|
||||||
|
let mut nonce_account = nonce::create_account(1).into_inner();
|
||||||
|
let state = state_from_account(&nonce_account).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
data_from_state(&state),
|
||||||
|
Err(CliNonceError::InvalidStateForOperation)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
data_from_account(&nonce_account),
|
||||||
|
Err(CliNonceError::InvalidStateForOperation)
|
||||||
|
);
|
||||||
|
|
||||||
|
let data = nonce::state::Data {
|
||||||
|
authority: Pubkey::new(&[1u8; 32]),
|
||||||
|
blockhash: Hash::new(&[42u8; 32]),
|
||||||
|
fee_calculator: FeeCalculator::new(42),
|
||||||
|
};
|
||||||
|
nonce_account
|
||||||
|
.set_state(&Versions::new_current(State::Initialized(data.clone())))
|
||||||
|
.unwrap();
|
||||||
|
let state = state_from_account(&nonce_account).unwrap();
|
||||||
|
assert_eq!(data_from_state(&state), Ok(&data));
|
||||||
|
assert_eq!(data_from_account(&nonce_account), Ok(data));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,15 @@ use serde_json::Value;
|
||||||
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
||||||
use solana_cli::{
|
use solana_cli::{
|
||||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand},
|
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand},
|
||||||
|
nonce,
|
||||||
offline::{parse_sign_only_reply_string, BlockhashQuery},
|
offline::{parse_sign_only_reply_string, BlockhashQuery},
|
||||||
};
|
};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_core::validator::TestValidator;
|
use solana_core::validator::TestValidator;
|
||||||
use solana_faucet::faucet::run_local_faucet;
|
use solana_faucet::faucet::run_local_faucet;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut,
|
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
nonce,
|
nonce::State as NonceState,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
};
|
};
|
||||||
|
@ -377,7 +377,7 @@ fn test_nonced_pay_tx() {
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
|
@ -408,14 +408,10 @@ fn test_nonced_pay_tx() {
|
||||||
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let bob_pubkey = Pubkey::new_rand();
|
let bob_pubkey = Pubkey::new_rand();
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
@ -432,14 +428,11 @@ fn test_nonced_pay_tx() {
|
||||||
check_balance(10, &rpc_client, &bob_pubkey);
|
check_balance(10, &rpc_client, &bob_pubkey);
|
||||||
|
|
||||||
// Verify that nonce has been used
|
// Verify that nonce has been used
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let nonce_hash2 = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
match nonce_state {
|
assert_ne!(nonce_hash, nonce_hash2);
|
||||||
nonce::State::Initialized(ref data) => assert_ne!(data.blockhash, nonce_hash),
|
|
||||||
_ => assert!(false, "Nonce is not initialized"),
|
|
||||||
}
|
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
||||||
use solana_cli::{
|
use solana_cli::{
|
||||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||||
|
nonce,
|
||||||
offline::{parse_sign_only_reply_string, BlockhashQuery},
|
offline::{parse_sign_only_reply_string, BlockhashQuery},
|
||||||
};
|
};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
|
@ -9,7 +10,7 @@ use solana_faucet::faucet::run_local_faucet;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
nonce,
|
nonce::State as NonceState,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{keypair_from_seed, Keypair, Signer},
|
signature::{keypair_from_seed, Keypair, Signer},
|
||||||
system_instruction::create_address_with_seed,
|
system_instruction::create_address_with_seed,
|
||||||
|
@ -457,7 +458,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
|
@ -499,14 +500,10 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Delegate stake
|
// Delegate stake
|
||||||
config.signers = vec![&config_keypair];
|
config.signers = vec![&config_keypair];
|
||||||
|
@ -524,14 +521,10 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Deactivate stake
|
// Deactivate stake
|
||||||
config.command = CliCommand::DeactivateStake {
|
config.command = CliCommand::DeactivateStake {
|
||||||
|
@ -704,7 +697,7 @@ fn test_stake_authorize() {
|
||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let nonce_account = Keypair::new();
|
let nonce_account = Keypair::new();
|
||||||
config.signers = vec![&default_signer, &nonce_account];
|
config.signers = vec![&default_signer, &nonce_account];
|
||||||
|
@ -717,14 +710,10 @@ fn test_stake_authorize() {
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nonced assignment of new online stake authority
|
// Nonced assignment of new online stake authority
|
||||||
let online_authority = Keypair::new();
|
let online_authority = Keypair::new();
|
||||||
|
@ -768,14 +757,11 @@ fn test_stake_authorize() {
|
||||||
_ => panic!("Unexpected stake state!"),
|
_ => panic!("Unexpected stake state!"),
|
||||||
};
|
};
|
||||||
assert_eq!(current_authority, online_authority_pubkey);
|
assert_eq!(current_authority, online_authority_pubkey);
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
let new_nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let new_nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
assert_ne!(nonce_hash, new_nonce_hash);
|
assert_ne!(nonce_hash, new_nonce_hash);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
|
@ -990,7 +976,7 @@ fn test_stake_split() {
|
||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
|
let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||||
config.signers = vec![&default_signer, &nonce_account];
|
config.signers = vec![&default_signer, &nonce_account];
|
||||||
|
@ -1004,14 +990,10 @@ fn test_stake_split() {
|
||||||
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nonced offline split
|
// Nonced offline split
|
||||||
let split_account = keypair_from_seed(&[2u8; 32]).unwrap();
|
let split_account = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||||
|
@ -1242,7 +1224,7 @@ fn test_stake_set_lockup() {
|
||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
|
let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||||
let nonce_account_pubkey = nonce_account.pubkey();
|
let nonce_account_pubkey = nonce_account.pubkey();
|
||||||
|
@ -1257,14 +1239,10 @@ fn test_stake_set_lockup() {
|
||||||
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey);
|
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey);
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account_pubkey).unwrap();
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nonced offline set lockup
|
// Nonced offline set lockup
|
||||||
let lockup = LockupArgs {
|
let lockup = LockupArgs {
|
||||||
|
@ -1359,7 +1337,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
|
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
|
||||||
let nonce_pubkey = nonce_account.pubkey();
|
let nonce_pubkey = nonce_account.pubkey();
|
||||||
|
@ -1373,14 +1351,10 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create stake account offline
|
// Create stake account offline
|
||||||
let stake_keypair = keypair_from_seed(&[4u8; 32]).unwrap();
|
let stake_keypair = keypair_from_seed(&[4u8; 32]).unwrap();
|
||||||
|
@ -1423,14 +1397,10 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||||
check_balance(50_000, &rpc_client, &stake_pubkey);
|
check_balance(50_000, &rpc_client, &stake_pubkey);
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Offline, nonced stake-withdraw
|
// Offline, nonced stake-withdraw
|
||||||
let recipient = keypair_from_seed(&[5u8; 32]).unwrap();
|
let recipient = keypair_from_seed(&[5u8; 32]).unwrap();
|
||||||
|
@ -1466,14 +1436,10 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||||
check_balance(42, &rpc_client, &recipient_pubkey);
|
check_balance(42, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create another stake account. This time with seed
|
// Create another stake account. This time with seed
|
||||||
let seed = "seedy";
|
let seed = "seedy";
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
|
||||||
use solana_cli::{
|
use solana_cli::{
|
||||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||||
|
nonce,
|
||||||
offline::{parse_sign_only_reply_string, BlockhashQuery},
|
offline::{parse_sign_only_reply_string, BlockhashQuery},
|
||||||
};
|
};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_core::validator::{TestValidator, TestValidatorOptions};
|
use solana_core::validator::{TestValidator, TestValidatorOptions};
|
||||||
use solana_faucet::faucet::run_local_faucet;
|
use solana_faucet::faucet::run_local_faucet;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut,
|
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
nonce,
|
nonce::State as NonceState,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{keypair_from_seed, Keypair, Signer},
|
signature::{keypair_from_seed, Keypair, Signer},
|
||||||
};
|
};
|
||||||
|
@ -120,7 +120,7 @@ fn test_transfer() {
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
|
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
config.signers = vec![&default_signer, &nonce_account];
|
config.signers = vec![&default_signer, &nonce_account];
|
||||||
config.command = CliCommand::CreateNonceAccount {
|
config.command = CliCommand::CreateNonceAccount {
|
||||||
|
@ -133,14 +133,10 @@ fn test_transfer() {
|
||||||
check_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
check_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nonced transfer
|
// Nonced transfer
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
@ -157,14 +153,10 @@ fn test_transfer() {
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||||
check_balance(30, &rpc_client, &recipient_pubkey);
|
check_balance(30, &rpc_client, &recipient_pubkey);
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let new_nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let new_nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
assert_ne!(nonce_hash, new_nonce_hash);
|
assert_ne!(nonce_hash, new_nonce_hash);
|
||||||
|
|
||||||
// Assign nonce authority to offline
|
// Assign nonce authority to offline
|
||||||
|
@ -178,14 +170,10 @@ fn test_transfer() {
|
||||||
check_balance(49_975 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
check_balance(49_975 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.convert_to_current();
|
.blockhash;
|
||||||
let nonce_hash = match nonce_state {
|
|
||||||
nonce::State::Initialized(ref data) => data.blockhash,
|
|
||||||
_ => panic!("Nonce is not initialized"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Offline, nonced transfer
|
// Offline, nonced transfer
|
||||||
offline.signers = vec![&default_offline_signer];
|
offline.signers = vec![&default_offline_signer];
|
||||||
|
|
Loading…
Reference in New Issue