Foreign API: Bulk block request (#3751)

* remove non-mainnet check for PIBD requests

* add first version of get blocks function

* .\api\src\foreign.rs

* add a max value to get_blocks, documentation

* typo

* updates based on feedback
This commit is contained in:
Yeastplume 2023-06-12 09:41:08 +01:00 committed by GitHub
parent 0649ba9645
commit 01370d74ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 370 additions and 3 deletions

View file

@ -23,12 +23,12 @@ use crate::handlers::pool_api::PoolHandler;
use crate::handlers::transactions_api::TxHashSetHandler;
use crate::handlers::version_api::VersionHandler;
use crate::pool::{self, BlockChain, PoolAdapter, PoolEntry};
use crate::rest::*;
use crate::types::{
BlockHeaderPrintable, BlockPrintable, LocatedTxKernel, OutputListing, OutputPrintable, Tip,
Version,
};
use crate::util::RwLock;
use crate::{rest::*, BlockListing};
use std::sync::Weak;
/// Main interface into all node API functions.
@ -139,6 +139,36 @@ where
block_handler.get_block(&hash, include_proof, include_merkle_proof)
}
/// Returns a [`BlockListing`](types/struct.BlockListing.html) of available blocks
/// between `min_height` and `max_height`
/// The method will query the database for blocks starting at the block height `min_height`
/// and continue until `max_height`, skipping any blocks that aren't available.
///
/// # Arguments
/// * `start_height` - starting height to lookup.
/// * `end_height` - ending height to to lookup.
/// * 'max` - The max number of blocks to return.
/// Note this is overriden with BLOCK_TRANSFER_LIMIT if BLOCK_TRANSFER_LIMIT is exceeded
///
/// # Returns
/// * Result Containing:
/// * A [`BlockListing`](types/struct.BlockListing.html)
/// * or [`Error`](struct.Error.html) if an error is encountered.
///
pub fn get_blocks(
&self,
start_height: u64,
end_height: u64,
max: u64,
include_proof: Option<bool>,
) -> Result<BlockListing, Error> {
let block_handler = BlockHandler {
chain: self.chain.clone(),
};
block_handler.get_blocks(start_height, end_height, max, include_proof)
}
/// Returns the node version and block header version (used by grin-wallet).
///
/// # Returns

View file

@ -21,8 +21,8 @@ use crate::pool::PoolEntry;
use crate::pool::{BlockChain, PoolAdapter};
use crate::rest::Error;
use crate::types::{
BlockHeaderPrintable, BlockPrintable, LocatedTxKernel, OutputListing, OutputPrintable, Tip,
Version,
BlockHeaderPrintable, BlockListing, BlockPrintable, LocatedTxKernel, OutputListing,
OutputPrintable, Tip, Version,
};
use crate::util;
@ -246,6 +246,251 @@ pub trait ForeignRpc: Sync + Send {
commit: Option<String>,
) -> Result<BlockPrintable, Error>;
/**
Networked version of [Foreign::get_blocks](struct.Foreign.html#method.get_blocks).
# Json rpc example
```
# grin_api::doctest_helper_json_rpc_foreign_assert_response!(
# r#"
{
"jsonrpc": "2.0",
"method": "get_blocks",
"params": [2299309, 2300309, 2, false],
"id": 1
}
# "#
# ,
# r#"
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"Ok": {
"blocks": [
{
"header": {
"cuckoo_solution": [
20354215,
100524565,
169529296,
259818619,
261952555,
265003136,
290685286,
307792709,
329993483,
331550733,
478902211,
707186317,
717277083,
742312701,
763869950,
785680094,
791217416,
1156641404,
1244452354,
1277970471,
1405106926,
1663783361,
1701259732,
1795507572,
1845900835,
2060172013,
2067055232,
2169213199,
2191128830,
2253855427,
2626425322,
2678973678,
2815586448,
2921010487,
3042894274,
3103031603,
3492595971,
3603041347,
3853538391,
3974438280,
4199558832,
4262968379
],
"edge_bits": 32,
"hash": "0004331bb122685f12644e40b163e4557951b2b835ad2493502750ea787af7cc",
"height": 2299309,
"kernel_mmr_size": 8568165,
"kernel_root": "6b4adb9ee193ad043910b5a8c1bac0864ab99f57845101a3b422031bcf5c2ce1",
"nonce": 4185528505858938389,
"output_mmr_size": 13524183,
"output_root": "b642891741b56adaf7762813490d161377d0fbf7b47170d235beef33c25a4d77",
"prev_root": "a0ba3206b6a8089ef05690d40767c41cc0514eaa5031ebce1960a7cc2edcc211",
"previous": "000207548609a9007eacd7dfcdc8006252d6b1ad70864ea8ddebe4ca9e82bd74",
"range_proof_root": "d8cefda00f325fd9a1223454f23276b73d8a1d7c72ec74cdfb9bdf5c77a04dee",
"secondary_scaling": 0,
"timestamp": "2023-06-05T20:18:45+00:00",
"total_difficulty": 2072532663425232,
"total_kernel_offset": "b0a0c21326532b4a91c18d2355aedca4d8ed68b77db9882feb85da8120b4f533",
"version": 5
},
"inputs": [
"092b140b1500812ac58ef68c17a2bbf2ec3531bcf0ce4dc32bbf8a29351d1784d7",
"083b72230921abeacd637dae8505233ab035c20dff1bfdab5ff5bb41b2f5238458"
],
"kernels": [
{
"excess": "08ab720dc374f099e6726e2dceada508a0331bb1f13b8a4e56afde83ff42f7a351",
"excess_sig": "6858120e9758d7587e27fd5dc9c26117a2ce0d5a7d871ce805e03eb494bfa1f86a27991865b3ab709064c43692433fd58f008c3bba2c88ad5f95a0c8ff3cf11f",
"features": "Plain",
"fee": 23500000,
"fee_shift": 0,
"lock_height": 0
},
{
"excess": "08d0a44b22952b03b29e3d88391102c281dcab4763def22cab65ed45e35b9078e8",
"excess_sig": "32f91d5671e334a87843a8b02c550c9e0fbdfe507ee62417cc123b5078d7884701a42e257357a1bed9dc4a8e07540b1629e9fa95a05c44adb5cb001c8fb777ee",
"features": "Coinbase",
"fee": 0,
"fee_shift": 0,
"lock_height": 0
}
],
"outputs": [
{
"block_height": 2299309,
"commit": "0857c94df51dd226fa0c5920aae6d73d069603f973b2e06551698c6d39fdc2c192",
"merkle_proof": null,
"mmr_index": 13524176,
"output_type": "Coinbase",
"proof": null,
"proof_hash": "0937291a8a3c81cea4421fa0d0b291aacb5d46065cfd93747a15f58d99d781b6",
"spent": false
},
{
"block_height": 2299309,
"commit": "08d4681b904695edee6e183cd40564ea0f5589b35d4d386da2eb980a6a92b1b307",
"merkle_proof": null,
"mmr_index": 0,
"output_type": "Transaction",
"proof": null,
"proof_hash": "41694ab6dcd9b1664ca28e79c3302144b99a4c1cb45d13c8728604c1d26e37bf",
"spent": true
},
{
"block_height": 2299309,
"commit": "08255a260a65fc87cfd924780d896eaadb42468b0fe3ba6adeace378793b5d8172",
"merkle_proof": null,
"mmr_index": 13524182,
"output_type": "Transaction",
"proof": null,
"proof_hash": "58c77a5716ec4806dbddac64a83d6e4351b6eeffca391be1b11ec74aac0514dc",
"spent": false
}
]
},
{
"header": {
"cuckoo_solution": [
898450,
353949138,
440882514,
500154010,
555236503,
615120852,
740100750,
754668484,
1056458121,
1071299788,
1130460099,
1414281857,
1444894533,
1481124421,
1551877341,
1666859923,
1682642953,
1837365586,
1845508478,
1872787697,
2040619654,
2078971700,
2104947318,
2206501084,
2233951742,
2360961460,
2378988856,
2402500295,
2438384422,
2532261092,
2879360933,
3011869457,
3023365279,
3412207020,
3509607650,
3793770861,
3850043972,
3873426868,
3965579806,
4007877324,
4090157476,
4141650723
],
"edge_bits": 32,
"hash": "00006871e1fb8e7dddcc46343d7fbba14d08946c67b4568f3c2e98ec8c554ae9",
"height": 2299310,
"kernel_mmr_size": 8568166,
"kernel_root": "87184dc2f9efa6467ce797191c5d3ef086403d0103ba0b5adc6a71ed203a053c",
"nonce": 13726392224838330049,
"output_mmr_size": 13524184,
"output_root": "9570fbccef29609c5d3c68b07771bf4e7e80d0b139d9bd0215d1e9d1aaaed813",
"prev_root": "df1c67366b9cdd8deea570534a00a320748899e146288be067c0f402038e6aa0",
"previous": "0004331bb122685f12644e40b163e4557951b2b835ad2493502750ea787af7cc",
"range_proof_root": "987d7aff01e201269d4c6b00e885b9ed9c10f47205edd7727e3490aab953ca80",
"secondary_scaling": 0,
"timestamp": "2023-06-05T20:19:27+00:00",
"total_difficulty": 2072532872584027,
"total_kernel_offset": "b0a0c21326532b4a91c18d2355aedca4d8ed68b77db9882feb85da8120b4f533",
"version": 5
},
"inputs": [],
"kernels": [
{
"excess": "08224a7946a75071b127af45496ddd3fc438db325cc35c3e4b0fdf23ed27703dd8",
"excess_sig": "d8c81bd8130c30016e38655a32b4c7a1f8fffda34a736dd8cdbcad05d28d09e3708d1f01e21276747eb03f28b9f5a834cb0ef8532330183df2b10d47ae7e68c6",
"features": "Coinbase",
"fee": 0,
"fee_shift": 0,
"lock_height": 0
}
],
"outputs": [
{
"block_height": 2299310,
"commit": "09997d3c1eff72b7efa7bfb52032d713f5907755838c01a6e178a87a0ac170a279",
"merkle_proof": null,
"mmr_index": 13524184,
"output_type": "Coinbase",
"proof": null,
"proof_hash": "6c2c10af5c4b6d2bcf71084c2bd9685ae91427f03a8b78736ab27d6c5bc7e4db",
"spent": false
}
]
}
],
"last_retrieved_height": 2299310
}
}
}
# "#
# );
```
*/
fn get_blocks(
&self,
start_height: u64,
end_height: u64,
max: u64,
include_proof: Option<bool>,
) -> Result<BlockListing, Error>;
/**
Networked version of [Foreign::get_version](struct.Foreign.html#method.get_version).
@ -760,6 +1005,7 @@ where
}
Foreign::get_header(self, height, parsed_hash, commit)
}
fn get_block(
&self,
height: Option<u64>,
@ -775,6 +1021,16 @@ where
Foreign::get_block(self, height, parsed_hash, commit)
}
fn get_blocks(
&self,
start_height: u64,
end_height: u64,
max: u64,
include_proof: Option<bool>,
) -> Result<BlockListing, Error> {
Foreign::get_blocks(self, start_height, end_height, max, include_proof)
}
fn get_version(&self) -> Result<Version, Error> {
Foreign::get_version(self)
}

View file

@ -24,6 +24,8 @@ use hyper::{Body, Request, StatusCode};
use regex::Regex;
use std::sync::Weak;
pub const BLOCK_TRANSFER_LIMIT: u64 = 1000;
/// Gets block headers given either a hash or height or an output commit.
/// GET /v1/headers/<hash>
/// GET /v1/headers/<height>
@ -138,6 +140,69 @@ impl BlockHandler {
.map_err(|_| Error::Internal("chain error".to_owned()))
}
pub fn get_blocks(
&self,
mut start_height: u64,
end_height: u64,
mut max: u64,
include_proof: Option<bool>,
) -> Result<BlockListing, Error> {
// set a limit here
if max > BLOCK_TRANSFER_LIMIT {
max = BLOCK_TRANSFER_LIMIT;
}
let tail_height = self.get_tail_height()?;
if start_height < tail_height {
start_height = tail_height;
}
let mut result_set = BlockListing {
last_retrieved_height: 0,
blocks: vec![],
};
let mut block_count = 0;
for h in start_height..=end_height {
result_set.last_retrieved_height = h;
let hash = match self.parse_inputs(Some(h), None, None) {
Err(e) => {
if let Error::NotFound = e {
continue;
} else {
return Err(e);
}
}
Ok(h) => h,
};
let block_res = self.get_block(&hash, include_proof == Some(true), false);
match block_res {
Err(e) => {
if let Error::NotFound = e {
continue;
} else {
return Err(e);
}
}
Ok(b) => {
block_count += 1;
result_set.blocks.push(b);
}
}
if block_count == max {
break;
}
}
Ok(result_set)
}
pub fn get_tail_height(&self) -> Result<u64, Error> {
let chain = w(&self.chain)?;
Ok(chain.get_tail().map_err(|_| Error::NotFound)?.height)
}
fn get_compact_block(&self, h: &Hash) -> Result<CompactBlockPrintable, Error> {
let chain = w(&self.chain)?;
let block = chain.get_block(h).map_err(|_| Error::NotFound)?;

View file

@ -722,6 +722,15 @@ pub struct OutputListing {
pub outputs: Vec<OutputPrintable>,
}
// For traversing a set of all available blocks
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BlockListing {
/// The last height retrieved
pub last_retrieved_height: u64,
/// A printable version of the retrieved Blocks
pub blocks: Vec<BlockPrintable>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct LocatedTxKernel {
pub tx_kernel: TxKernel,

View file

@ -1445,6 +1445,13 @@ impl Chain {
.map_err(|e| Error::StoreErr(e, "chain get block".to_owned()))
}
/// Gets the earliest stored block (tail)
pub fn get_tail(&self) -> Result<Tip, Error> {
self.store
.tail()
.map_err(|e| Error::StoreErr(e, "chain get tail".to_owned()))
}
/// Gets a block header by hash
pub fn get_block_header(&self, h: &Hash) -> Result<BlockHeader, Error> {
self.store