Add mechanism to get blockhash's last valid slot (#10239)

automerge
This commit is contained in:
Tyera Eulberg 2020-05-26 13:06:21 -06:00 committed by GitHub
parent 1bfc4c1489
commit 4e431bc818
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 160 additions and 17 deletions

View File

@ -614,26 +614,46 @@ impl RpcClient {
}
pub fn get_recent_blockhash(&self) -> ClientResult<(Hash, FeeCalculator)> {
Ok(self
let (blockhash, fee_calculator, _last_valid_slot) = self
.get_recent_blockhash_with_commitment(CommitmentConfig::default())?
.value)
.value;
Ok((blockhash, fee_calculator))
}
pub fn get_recent_blockhash_with_commitment(
&self,
commitment_config: CommitmentConfig,
) -> RpcResult<(Hash, FeeCalculator)> {
let Response {
) -> RpcResult<(Hash, FeeCalculator, Slot)> {
let (context, blockhash, fee_calculator, last_valid_slot) = if let Ok(Response {
context,
value:
RpcFees {
blockhash,
fee_calculator,
last_valid_slot,
},
}) =
self.send::<Response<RpcFees>>(RpcRequest::GetFees, json!([commitment_config]))
{
(context, blockhash, fee_calculator, last_valid_slot)
} else if let Ok(Response {
context,
value:
RpcBlockhashFeeCalculator {
blockhash,
fee_calculator,
},
} = self.send::<Response<RpcBlockhashFeeCalculator>>(
}) = self.send::<Response<RpcBlockhashFeeCalculator>>(
RpcRequest::GetRecentBlockhash,
json!([commitment_config]),
)?;
) {
(context, blockhash, fee_calculator, 0)
} else {
return Err(ClientError::new_with_request(
RpcError::ParseError("RpcBlockhashFeeCalculator or RpcFees".to_string()).into(),
RpcRequest::GetRecentBlockhash,
));
};
let blockhash = blockhash.parse().map_err(|_| {
ClientError::new_with_request(
@ -643,7 +663,7 @@ impl RpcClient {
})?;
Ok(Response {
context,
value: (blockhash, fee_calculator),
value: (blockhash, fee_calculator, last_valid_slot),
})
}

View File

@ -16,15 +16,17 @@ pub enum RpcRequest {
GetConfirmedTransaction,
GetEpochInfo,
GetEpochSchedule,
GetFeeCalculatorForBlockhash,
GetFeeRateGovernor,
GetFees,
GetGenesisHash,
GetIdentity,
GetInflation,
GetLargestAccounts,
GetLeaderSchedule,
GetMinimumBalanceForRentExemption,
GetProgramAccounts,
GetRecentBlockhash,
GetFeeCalculatorForBlockhash,
GetFeeRateGovernor,
GetSignatureStatuses,
GetSlot,
GetSlotLeader,
@ -37,13 +39,12 @@ pub enum RpcRequest {
GetTransactionCount,
GetVersion,
GetVoteAccounts,
MinimumLedgerSlot,
RegisterNode,
RequestAirdrop,
SendTransaction,
SimulateTransaction,
SignVote,
GetMinimumBalanceForRentExemption,
MinimumLedgerSlot,
}
impl fmt::Display for RpcRequest {
@ -61,15 +62,17 @@ impl fmt::Display for RpcRequest {
RpcRequest::GetConfirmedTransaction => "getConfirmedTransaction",
RpcRequest::GetEpochInfo => "getEpochInfo",
RpcRequest::GetEpochSchedule => "getEpochSchedule",
RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
RpcRequest::GetFees => "getFees",
RpcRequest::GetGenesisHash => "getGenesisHash",
RpcRequest::GetIdentity => "getIdentity",
RpcRequest::GetInflation => "getInflation",
RpcRequest::GetLargestAccounts => "getLargestAccounts",
RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
RpcRequest::GetProgramAccounts => "getProgramAccounts",
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
RpcRequest::GetSlot => "getSlot",
RpcRequest::GetSlotLeader => "getSlotLeader",
@ -82,13 +85,12 @@ impl fmt::Display for RpcRequest {
RpcRequest::GetTransactionCount => "getTransactionCount",
RpcRequest::GetVersion => "getVersion",
RpcRequest::GetVoteAccounts => "getVoteAccounts",
RpcRequest::MinimumLedgerSlot => "minimumLedgerSlot",
RpcRequest::RegisterNode => "registerNode",
RpcRequest::RequestAirdrop => "requestAirdrop",
RpcRequest::SendTransaction => "sendTransaction",
RpcRequest::SimulateTransaction => "simulateTransaction",
RpcRequest::SignVote => "signVote",
RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
RpcRequest::MinimumLedgerSlot => "minimumLedgerSlot",
};
write!(f, "{}", method)

View File

@ -35,6 +35,14 @@ pub struct RpcBlockhashFeeCalculator {
pub fee_calculator: FeeCalculator,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcFees {
pub blockhash: String,
pub fee_calculator: FeeCalculator,
pub last_valid_slot: Slot,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcFeeCalculator {

View File

@ -441,7 +441,7 @@ impl SyncClient for ThinClient {
match recent_blockhash {
Ok(Response { value, .. }) => {
self.optimizer.report(index, duration_as_ms(&now.elapsed()));
Ok(value)
Ok((value.0, value.1))
}
Err(e) => {
self.optimizer.report(index, std::u64::MAX);

View File

@ -207,6 +207,22 @@ impl JsonRpcRequestProcessor {
)
}
fn get_fees(&self, commitment: Option<CommitmentConfig>) -> RpcResponse<RpcFees> {
let bank = &*self.bank(commitment)?;
let (blockhash, fee_calculator) = bank.confirmed_last_blockhash();
let last_valid_slot = bank
.get_blockhash_last_valid_slot(&blockhash)
.expect("bank blockhash queue should contain blockhash");
new_response(
bank,
RpcFees {
blockhash: blockhash.to_string(),
fee_calculator,
last_valid_slot,
},
)
}
fn get_fee_calculator_for_blockhash(
&self,
blockhash: &Hash,
@ -793,6 +809,13 @@ pub trait RpcSol {
commitment: Option<CommitmentConfig>,
) -> RpcResponse<RpcBlockhashFeeCalculator>;
#[rpc(meta, name = "getFees")]
fn get_fees(
&self,
meta: Self::Metadata,
commitment: Option<CommitmentConfig>,
) -> RpcResponse<RpcFees>;
#[rpc(meta, name = "getFeeCalculatorForBlockhash")]
fn get_fee_calculator_for_blockhash(
&self,
@ -1126,6 +1149,15 @@ impl RpcSol for RpcSolImpl {
.get_recent_blockhash(commitment)
}
fn get_fees(
&self,
meta: Self::Metadata,
commitment: Option<CommitmentConfig>,
) -> RpcResponse<RpcFees> {
debug!("get_fees rpc request received");
meta.request_processor.read().unwrap().get_fees(commitment)
}
fn get_fee_calculator_for_blockhash(
&self,
meta: Self::Metadata,
@ -1571,6 +1603,7 @@ pub mod tests {
get_tmp_ledger_path,
};
use solana_sdk::{
clock::MAX_RECENT_BLOCKHASHES,
fee_calculator::DEFAULT_BURN_PERCENT,
hash::{hash, Hash},
instruction::InstructionError,
@ -2517,6 +2550,38 @@ pub mod tests {
assert_eq!(expected, result);
}
#[test]
fn test_rpc_get_fees() {
let bob_pubkey = Pubkey::new_rand();
let RpcHandler {
io,
meta,
blockhash,
..
} = start_rpc_handler_with_tx(&bob_pubkey);
let req = r#"{"jsonrpc":"2.0","id":1,"method":"getFees"}"#;
let res = io.handle_request_sync(&req, meta);
let expected = json!({
"jsonrpc": "2.0",
"result": {
"context":{"slot":0},
"value":{
"blockhash": blockhash.to_string(),
"feeCalculator": {
"lamportsPerSignature": 0,
},
"lastValidSlot": MAX_RECENT_BLOCKHASHES,
}},
"id": 1
});
let expected: Response =
serde_json::from_value(expected).expect("expected response deserialization");
let result: Response = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
assert_eq!(expected, result);
}
#[test]
fn test_rpc_get_fee_calculator_for_blockhash() {
let bob_pubkey = Pubkey::new_rand();

View File

@ -27,6 +27,7 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
* [getEpochSchedule](jsonrpc-api.md#getepochschedule)
* [getFeeCalculatorForBlockhash](jsonrpc-api.md#getfeecalculatorforblockhash)
* [getFeeRateGovernor](jsonrpc-api.md#getfeerategovernor)
* [getFees](jsonrpc-api.md#getfees)
* [getFirstAvailableBlock](jsonrpc-api.md#getfirstavailableblock)
* [getGenesisHash](jsonrpc-api.md#getgenesishash)
* [getIdentity](jsonrpc-api.md#getidentity)
@ -538,6 +539,34 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
{"jsonrpc":"2.0","result":{"context":{"slot":54},"value":{"feeRateGovernor":{"burnPercent":50,"maxLamportsPerSignature":100000,"minLamportsPerSignature":5000,"targetLamportsPerSignature":10000,"targetSignaturesPerSlot":20000}}},"id":1}
```
### getFees
Returns a recent block hash from the ledger, a fee schedule that can be used to
compute the cost of submitting a transaction using it, and the last slot in
which the blockhash will be valid.
#### Parameters:
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
#### Results:
The result will be an RpcResponse JSON object with `value` set to a JSON object with the following fields:
* `blockhash: <string>` - a Hash as base-58 encoded string
* `feeCalculator: <object>` - FeeCalculator object, the fee schedule for this block hash
* `lastValidSlot: <u64>` - last slot in which a blockhash will be valid
#### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getFees"}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":{"context":{"slot":1},"value":{"blockhash":"CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR","feeCalculator":{lamportsPerSignature":5000},"lastValidSlot":297}},"id":1}
```
### getFirstAvailableBlock
Returns the slot of the lowest confirmed block that has not been purged from the ledger
@ -765,7 +794,7 @@ An RpcResponse containing a JSON object consisting of a string blockhash and Fee
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getRecentBlockhash"}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":{"context":{"slot":1},"value":{"blockhash":"CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR","feeCalculator":{"burnPercent":50,"lamportsPerSignature":5000,"maxLamportsPerSignature":100000,"minLamportsPerSignature":5000,"targetLamportsPerSignature":10000,"targetSignaturesPerSlot":20000}}},"id":1}
{"jsonrpc":"2.0","result":{"context":{"slot":1},"value":{"blockhash":"CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR","feeCalculator":{"lamportsPerSignature":5000}}},"id":1}
```
### getSignatureStatuses

View File

@ -936,6 +936,15 @@ impl Bank {
&self.fee_rate_governor
}
pub fn get_blockhash_last_valid_slot(&self, blockhash: &Hash) -> Option<Slot> {
let blockhash_queue = self.blockhash_queue.read().unwrap();
// This calculation will need to be updated to consider epoch boundaries if BlockhashQueue
// length is made variable by epoch
blockhash_queue
.get_hash_age(blockhash)
.map(|age| self.slot + blockhash_queue.len() as u64 - age)
}
pub fn confirmed_last_blockhash(&self) -> (Hash, FeeCalculator) {
const NUM_BLOCKHASH_CONFIRMATIONS: usize = 3;

View File

@ -58,6 +58,12 @@ impl BlockhashQueue {
.map(|age| self.hash_height - age.hash_height <= max_age as u64)
}
pub fn get_hash_age(&self, hash: &Hash) -> Option<u64> {
self.ages
.get(hash)
.map(|age| self.hash_height - age.hash_height)
}
/// check if hash is valid
#[cfg(test)]
pub fn check_hash(&self, hash: Hash) -> bool {
@ -119,6 +125,10 @@ impl BlockhashQueue {
.iter()
.map(|(k, v)| recent_blockhashes::IterItem(v.hash_height, k, &v.fee_calculator))
}
pub fn len(&self) -> usize {
self.max_age
}
}
#[cfg(test)]
mod tests {