Extend GetBlockHash RPC API to include the fee scehdule for using the returned blockhash (#4222)
This commit is contained in:
@ -2,6 +2,7 @@ use crate::client_error::ClientError;
|
||||
use crate::generic_rpc_client_request::GenericRpcClientRequest;
|
||||
use crate::rpc_request::RpcRequest;
|
||||
use serde_json::{Number, Value};
|
||||
use solana_sdk::fee_calculator::FeeCalculator;
|
||||
use solana_sdk::transaction::{self, TransactionError};
|
||||
|
||||
pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8";
|
||||
@ -44,7 +45,10 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
|
||||
let n = if self.url == "airdrop" { 0 } else { 50 };
|
||||
Value::Number(Number::from(n))
|
||||
}
|
||||
RpcRequest::GetRecentBlockhash => Value::String(PUBKEY.to_string()),
|
||||
RpcRequest::GetRecentBlockhash => Value::Array(vec![
|
||||
Value::String(PUBKEY.to_string()),
|
||||
serde_json::to_value(FeeCalculator::default()).unwrap(),
|
||||
]),
|
||||
RpcRequest::GetSignatureStatus => {
|
||||
let response: Option<transaction::Result<()>> = if self.url == "account_in_use" {
|
||||
Some(Err(TransactionError::AccountInUse))
|
||||
|
@ -7,6 +7,7 @@ use bincode::serialize;
|
||||
use log::*;
|
||||
use serde_json::{json, Value};
|
||||
use solana_sdk::account::Account;
|
||||
use solana_sdk::fee_calculator::FeeCalculator;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::{KeypairUtil, Signature};
|
||||
@ -186,7 +187,7 @@ impl RpcClient {
|
||||
send_retries -= 1;
|
||||
|
||||
// Re-sign any failed transactions with a new blockhash and retry
|
||||
let blockhash =
|
||||
let (blockhash, _fee_calculator) =
|
||||
self.get_new_blockhash(&transactions_signatures[0].0.message().recent_blockhash)?;
|
||||
transactions = transactions_signatures
|
||||
.into_iter()
|
||||
@ -203,7 +204,8 @@ impl RpcClient {
|
||||
tx: &mut Transaction,
|
||||
signer_keys: &[&T],
|
||||
) -> Result<(), ClientError> {
|
||||
let blockhash = self.get_new_blockhash(&tx.message().recent_blockhash)?;
|
||||
let (blockhash, _fee_calculator) =
|
||||
self.get_new_blockhash(&tx.message().recent_blockhash)?;
|
||||
tx.sign(signer_keys, blockhash);
|
||||
Ok(())
|
||||
}
|
||||
@ -234,9 +236,11 @@ impl RpcClient {
|
||||
trace!("Response account {:?} {:?}", pubkey, account);
|
||||
Ok(account)
|
||||
})
|
||||
.map_err(|error| {
|
||||
debug!("Response account {}: None (error: {:?})", pubkey, error);
|
||||
io::Error::new(io::ErrorKind::Other, "AccountNotFound")
|
||||
.map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("AccountNotFound: pubkey={}: {}", pubkey, err),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@ -264,13 +268,15 @@ impl RpcClient {
|
||||
)
|
||||
})?;
|
||||
|
||||
serde_json::from_value(response).map_err(|error| {
|
||||
debug!("ParseError: get_transaction_count: {}", error);
|
||||
io::Error::new(io::ErrorKind::Other, "GetTransactionCount parse failure")
|
||||
serde_json::from_value(response).map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("GetTransactionCount parse failure: {}", err),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_recent_blockhash(&self) -> io::Result<Hash> {
|
||||
pub fn get_recent_blockhash(&self) -> io::Result<(Hash, FeeCalculator)> {
|
||||
let response = self
|
||||
.client
|
||||
.send(&RpcRequest::GetRecentBlockhash, None, 0)
|
||||
@ -281,21 +287,29 @@ impl RpcClient {
|
||||
)
|
||||
})?;
|
||||
|
||||
response
|
||||
.as_str()
|
||||
.ok_or_else(|| {
|
||||
io::Error::new(io::ErrorKind::Other, "GetRecentBlockhash parse failure")
|
||||
})?
|
||||
.parse()
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::Other, "GetRecentBlockhash parse failure"))
|
||||
let (blockhash, fee_calculator) =
|
||||
serde_json::from_value::<(String, FeeCalculator)>(response).map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("GetRecentBlockhash parse failure: {:?}", err),
|
||||
)
|
||||
})?;
|
||||
|
||||
let blockhash = blockhash.parse().map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("GetRecentBlockhash parse failure: {:?}", err),
|
||||
)
|
||||
})?;
|
||||
Ok((blockhash, fee_calculator))
|
||||
}
|
||||
|
||||
pub fn get_new_blockhash(&self, blockhash: &Hash) -> io::Result<Hash> {
|
||||
pub fn get_new_blockhash(&self, blockhash: &Hash) -> io::Result<(Hash, FeeCalculator)> {
|
||||
let mut num_retries = 10;
|
||||
while num_retries > 0 {
|
||||
if let Ok(new_blockhash) = self.get_recent_blockhash() {
|
||||
if let Ok((new_blockhash, fee_calculator)) = self.get_recent_blockhash() {
|
||||
if new_blockhash != *blockhash {
|
||||
return Ok(new_blockhash);
|
||||
return Ok((new_blockhash, fee_calculator));
|
||||
}
|
||||
}
|
||||
debug!("Got same blockhash ({:?}), will retry...", blockhash);
|
||||
@ -454,24 +468,22 @@ impl RpcClient {
|
||||
Some(params.clone()),
|
||||
1,
|
||||
)
|
||||
.map_err(|error| {
|
||||
debug!(
|
||||
"Response get_num_blocks_since_signature_confirmation: {:?}",
|
||||
error
|
||||
);
|
||||
.map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"GetNumBlocksSinceSignatureConfirmation request failure",
|
||||
format!(
|
||||
"GetNumBlocksSinceSignatureConfirmation request failure: {}",
|
||||
err
|
||||
),
|
||||
)
|
||||
})?;
|
||||
serde_json::from_value(response).map_err(|error| {
|
||||
debug!(
|
||||
"ParseError: get_num_blocks_since_signature_confirmation: {}",
|
||||
error
|
||||
);
|
||||
serde_json::from_value(response).map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"GetNumBlocksSinceSignatureConfirmation parse failure",
|
||||
format!(
|
||||
"GetNumBlocksSinceSignatureConfirmation parse failure: {}",
|
||||
err
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
@ -578,7 +590,7 @@ mod tests {
|
||||
// Send erroneous parameter
|
||||
let blockhash = rpc_client.retry_make_rpc_request(
|
||||
&RpcRequest::GetRecentBlockhash,
|
||||
Some(json!("paramter")),
|
||||
Some(json!("parameter")),
|
||||
0,
|
||||
);
|
||||
assert_eq!(blockhash.is_err(), true);
|
||||
@ -646,13 +658,12 @@ mod tests {
|
||||
let vec = bs58::decode(PUBKEY).into_vec().unwrap();
|
||||
let expected_blockhash = Hash::new(&vec);
|
||||
|
||||
let blockhash = dbg!(rpc_client.get_recent_blockhash()).expect("blockhash ok");
|
||||
let (blockhash, _fee_calculator) = rpc_client.get_recent_blockhash().expect("blockhash ok");
|
||||
assert_eq!(blockhash, expected_blockhash);
|
||||
|
||||
let rpc_client = RpcClient::new_mock("fails".to_string());
|
||||
|
||||
let blockhash = dbg!(rpc_client.get_recent_blockhash());
|
||||
assert!(blockhash.is_err());
|
||||
assert!(rpc_client.get_recent_blockhash().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -7,6 +7,7 @@ use crate::rpc_client::RpcClient;
|
||||
use bincode::{serialize_into, serialized_size};
|
||||
use log::*;
|
||||
use solana_sdk::client::{AsyncClient, Client, SyncClient};
|
||||
use solana_sdk::fee_calculator::FeeCalculator;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::instruction::Instruction;
|
||||
use solana_sdk::message::Message;
|
||||
@ -107,7 +108,8 @@ impl ThinClient {
|
||||
return Ok(transaction.signatures[0]);
|
||||
}
|
||||
info!("{} tries failed transfer to {}", x, self.transactions_addr);
|
||||
transaction.sign(keypairs, self.rpc_client.get_recent_blockhash()?);
|
||||
let (blockhash, _fee_calculator) = self.rpc_client.get_recent_blockhash()?;
|
||||
transaction.sign(keypairs, blockhash);
|
||||
}
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
@ -159,7 +161,7 @@ impl Client for ThinClient {
|
||||
|
||||
impl SyncClient for ThinClient {
|
||||
fn send_message(&self, keypairs: &[&Keypair], message: Message) -> TransportResult<Signature> {
|
||||
let blockhash = self.get_recent_blockhash()?;
|
||||
let (blockhash, _fee_calculator) = self.get_recent_blockhash()?;
|
||||
let mut transaction = Transaction::new(&keypairs, message, blockhash);
|
||||
let signature = self.send_and_confirm_transaction(keypairs, &mut transaction, 5, 0)?;
|
||||
Ok(signature)
|
||||
@ -210,9 +212,8 @@ impl SyncClient for ThinClient {
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
fn get_recent_blockhash(&self) -> TransportResult<Hash> {
|
||||
let recent_blockhash = self.rpc_client.get_recent_blockhash()?;
|
||||
Ok(recent_blockhash)
|
||||
fn get_recent_blockhash(&self) -> TransportResult<(Hash, FeeCalculator)> {
|
||||
Ok(self.rpc_client.get_recent_blockhash()?)
|
||||
}
|
||||
|
||||
fn get_transaction_count(&self) -> TransportResult<u64> {
|
||||
@ -235,9 +236,8 @@ impl SyncClient for ThinClient {
|
||||
Ok(self.rpc_client.poll_for_signature(signature)?)
|
||||
}
|
||||
|
||||
fn get_new_blockhash(&self, blockhash: &Hash) -> TransportResult<Hash> {
|
||||
let new_blockhash = self.rpc_client.get_new_blockhash(blockhash)?;
|
||||
Ok(new_blockhash)
|
||||
fn get_new_blockhash(&self, blockhash: &Hash) -> TransportResult<(Hash, FeeCalculator)> {
|
||||
Ok(self.rpc_client.get_new_blockhash(blockhash)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user