parent
1bfc4c1489
commit
4e431bc818
|
@ -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),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue