diff --git a/js/src/api/format/output.js b/js/src/api/format/output.js index ca1d54ede..e1887582a 100644 --- a/js/src/api/format/output.js +++ b/js/src/api/format/output.js @@ -66,6 +66,20 @@ export function outBlock (block) { return block; } +export function outChainStatus (status) { + if (status) { + Object.keys(status).forEach((key) => { + switch (key) { + case 'blockGap': + status[key] = status[key].map(outNumber); + break; + } + }); + } + + return status; +} + export function outDate (date) { return new Date(outNumber(date).toNumber() * 1000); } @@ -77,6 +91,7 @@ export function outHistogram (histogram) { case 'bucketBounds': case 'counts': histogram[key] = histogram[key].map(outNumber); + break; } }); } diff --git a/js/src/api/format/output.spec.js b/js/src/api/format/output.spec.js index 1958b57d8..ce50c69c1 100644 --- a/js/src/api/format/output.spec.js +++ b/js/src/api/format/output.spec.js @@ -16,7 +16,7 @@ import BigNumber from 'bignumber.js'; -import { outBlock, outAccountInfo, outAddress, outDate, outHistogram, outNumber, outPeers, outReceipt, outSyncing, outTransaction, outTrace } from './output'; +import { outBlock, outAccountInfo, outAddress, outChainStatus, outDate, outHistogram, outNumber, outPeers, outReceipt, outSyncing, outTransaction, outTrace } from './output'; import { isAddress, isBigNumber, isInstanceOf } from '../../../test/types'; describe('api/format/output', () => { @@ -114,6 +114,18 @@ describe('api/format/output', () => { }); }); + describe('outChainStatus', () => { + it('formats blockGap values', () => { + const status = { + blockGap: [0x1234, '0x5678'] + }; + + expect(outChainStatus(status)).to.deep.equal({ + blockGap: [new BigNumber(0x1234), new BigNumber(0x5678)] + }); + }); + }); + describe('outDate', () => { it('converts a second date in unix timestamp', () => { expect(outDate(0x57513668)).to.deep.equal(new Date('2016-06-03T07:48:56.000Z')); diff --git a/js/src/api/rpc/parity/parity.js b/js/src/api/rpc/parity/parity.js index 2605e41e5..816081643 100644 --- a/js/src/api/rpc/parity/parity.js +++ b/js/src/api/rpc/parity/parity.js @@ -15,7 +15,7 @@ // along with Parity. If not, see . import { inAddress, inData, inHex, inNumber16, inOptions } from '../../format/input'; -import { outAccountInfo, outAddress, outHistogram, outNumber, outPeers, outTransaction } from '../../format/output'; +import { outAccountInfo, outAddress, outChainStatus, outHistogram, outNumber, outPeers, outTransaction } from '../../format/output'; export default class Parity { constructor (transport) { @@ -44,6 +44,12 @@ export default class Parity { .execute('parity_addReservedPeer', encode); } + chainStatus () { + return this._transport + .execute('parity_chainStatus') + .then(outChainStatus); + } + changePassword (account, password, newPassword) { return this._transport .execute('parity_changePassword', inAddress(account), password, newPassword); diff --git a/js/src/api/rpc/parity/parity.spec.js b/js/src/api/rpc/parity/parity.spec.js index b58c8f85c..6b15a8fe1 100644 --- a/js/src/api/rpc/parity/parity.spec.js +++ b/js/src/api/rpc/parity/parity.spec.js @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import BigNumber from 'bignumber.js'; import { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc'; import { isBigNumber } from '../../../../test/types'; @@ -45,6 +46,22 @@ describe('api/rpc/parity', () => { }); }); + describe('chainStatus', () => { + it('retrieves the chain status', () => { + mockHttp([{ method: 'parity_chainStatus', reply: { + result: { + 'blockGap': [0x123, 0x456] + } + } }]); + + return instance.chainStatus().then((result) => { + expect(result).to.deep.equal({ + 'blockGap': [new BigNumber(0x123), new BigNumber(0x456)] + }); + }); + }); + }); + describe('gasFloorTarget', () => { it('returns the gasfloor, formatted', () => { mockHttp([{ method: 'parity_gasFloorTarget', reply: { result: '0x123456' } }]); diff --git a/js/src/jsonrpc/interfaces/parity.js b/js/src/jsonrpc/interfaces/parity.js index 3f36a93b3..db194a913 100644 --- a/js/src/jsonrpc/interfaces/parity.js +++ b/js/src/jsonrpc/interfaces/parity.js @@ -86,6 +86,22 @@ export default { } }, + chainStatus: { + desc: 'Returns the information on warp sync blocks', + params: [], + returns: { + type: Object, + desc: 'The status object', + details: { + blockGap: { + type: Array, + desc: 'Describes the gap in the blockchain, if there is one: (first, last)', + optional: true + } + } + } + }, + checkRequest: { desc: 'Returns the transactionhash of the requestId (received from parity_postTransaction) if the request was confirmed', params: [ diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index fbd4fe8ed..491bdde10 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -293,15 +293,13 @@ impl Eth for EthClient where let chain_info = client.chain_info(); let current_block = U256::from(chain_info.best_block_number); let highest_block = U256::from(status.highest_block_number.unwrap_or(status.start_block_number)); - let gap = chain_info.ancient_block_number.map(|x| U256::from(x + 1)) - .and_then(|first| chain_info.first_block_number.map(|last| (first, U256::from(last)))); + let info = SyncInfo { starting_block: status.start_block_number.into(), current_block: current_block.into(), highest_block: highest_block.into(), warp_chunks_amount: warp_chunks_amount.map(|x| U256::from(x as u64)).map(Into::into), warp_chunks_processed: warp_chunks_processed.map(|x| U256::from(x as u64)).map(Into::into), - block_gap: gap.map(|(x, y)| (x.into(), y.into())), }; Ok(SyncStatus::Info(info)) } else { diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 326dc9c2b..3c14ea39e 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -40,7 +40,7 @@ use v1::types::{ Peers, Transaction, RpcSettings, Histogram, TransactionStats, LocalTransactionStatus, BlockNumber, ConsensusCapability, VersionInfo, - OperationsInfo + OperationsInfo, ChainStatus, }; use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings}; use v1::helpers::dispatch::DEFAULT_MAC; @@ -385,4 +385,17 @@ impl Parity for ParityClient where let updater = take_weak!(self.updater); Ok(updater.info().map(Into::into)) } + + fn chain_status(&self) -> Result { + try!(self.active()); + + let chain_info = take_weak!(self.client).chain_info(); + + let gap = chain_info.ancient_block_number.map(|x| U256::from(x + 1)) + .and_then(|first| chain_info.first_block_number.map(|last| (first, U256::from(last)))); + + Ok(ChainStatus { + block_gap: gap.map(|(x, y)| (x.into(), y.into())), + }) + } } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 35790a29f..c0566589e 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -137,16 +137,15 @@ fn rpc_eth_syncing() { // "sync" to 1000 blocks. // causes TestBlockChainClient to return 1000 for its best block number. tester.add_blocks(1000, EachBlockWith::Nothing); - *tester.client.ancient_block.write() = Some((H256::new(), 5)); - *tester.client.first_block.write() = Some((H256::from(U256::from(1234)), 3333)); - let true_res = r#"{"jsonrpc":"2.0","result":{"blockGap":["0x6","0xd05"],"currentBlock":"0x3e8","highestBlock":"0x9c4","startingBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null},"id":1}"#; + + let true_res = r#"{"jsonrpc":"2.0","result":{"currentBlock":"0x3e8","highestBlock":"0x9c4","startingBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null},"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(true_res.to_owned())); *tester.client.ancient_block.write() = None; *tester.client.first_block.write() = None; - let snap_res = r#"{"jsonrpc":"2.0","result":{"blockGap":null,"currentBlock":"0x3e8","highestBlock":"0x9c4","startingBlock":"0x0","warpChunksAmount":"0x32","warpChunksProcessed":"0x18"},"id":1}"#; + let snap_res = r#"{"jsonrpc":"2.0","result":{"currentBlock":"0x3e8","highestBlock":"0x9c4","startingBlock":"0x0","warpChunksAmount":"0x32","warpChunksProcessed":"0x18"},"id":1}"#; tester.snapshot.set_status(RestorationStatus::Ongoing { state_chunks: 40, block_chunks: 10, diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index ca5c9b8db..2ab5ba2ce 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -421,3 +421,18 @@ fn rpc_parity_local_transactions() { assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } +#[test] +fn rpc_parity_chain_status() { + use util::{H256, U256}; + + let deps = Dependencies::new(); + let io = deps.default_client(); + + *deps.client.ancient_block.write() = Some((H256::default(), 5)); + *deps.client.first_block.write() = Some((H256::from(U256::from(1234)), 3333)); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_chainStatus", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockGap":["0x6","0xd05"]},"id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); +} diff --git a/rpc/src/v1/traits/parity.rs b/rpc/src/v1/traits/parity.rs index d0e3b16ee..9c8d61bf4 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -26,7 +26,7 @@ use v1::types::{ Peers, Transaction, RpcSettings, Histogram, TransactionStats, LocalTransactionStatus, BlockNumber, ConsensusCapability, VersionInfo, - OperationsInfo + OperationsInfo, ChainStatus, }; build_rpc_trait! { @@ -174,5 +174,9 @@ build_rpc_trait! { /// Get information concerning the latest releases if available. #[rpc(name = "parity_releasesInfo")] fn releases_info(&self) -> Result, Error>; + + /// Get the current chain status. + #[rpc(name = "parity_chainStatus")] + fn chain_status(&self) -> Result; } } diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 7c7c56c9a..954622a74 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -49,7 +49,10 @@ pub use self::filter::{Filter, FilterChanges}; pub use self::hash::{H64, H160, H256, H512, H520, H2048}; pub use self::index::Index; pub use self::log::Log; -pub use self::sync::{SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, PeerEthereumProtocolInfo, TransactionStats}; +pub use self::sync::{ + SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, PeerEthereumProtocolInfo, + TransactionStats, ChainStatus +}; pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus}; pub use self::transaction_request::TransactionRequest; pub use self::receipt::Receipt; @@ -59,4 +62,4 @@ pub use self::trace_filter::TraceFilter; pub use self::uint::{U128, U256}; pub use self::work::Work; pub use self::histogram::Histogram; -pub use self::consensus_status::*; \ No newline at end of file +pub use self::consensus_status::*; diff --git a/rpc/src/v1/types/sync.rs b/rpc/src/v1/types/sync.rs index 837b425d7..d0e3c7c99 100644 --- a/rpc/src/v1/types/sync.rs +++ b/rpc/src/v1/types/sync.rs @@ -37,9 +37,6 @@ pub struct SyncInfo { /// Warp sync snpashot chunks processed. #[serde(rename="warpChunksProcessed")] pub warp_chunks_processed: Option, - /// Describes the gap in the blockchain, if there is one: (first, last) - #[serde(rename="blockGap")] - pub block_gap: Option<(U256, U256)>, } /// Peers info @@ -162,17 +159,25 @@ impl From for TransactionStats { } } +/// Chain status. +#[derive(Default, Debug, Serialize)] +pub struct ChainStatus { + /// Describes the gap in the blockchain, if there is one: (first, last) + #[serde(rename="blockGap")] + pub block_gap: Option<(U256, U256)>, +} + #[cfg(test)] mod tests { use serde_json; use std::collections::BTreeMap; - use super::{SyncInfo, SyncStatus, Peers, TransactionStats}; + use super::{SyncInfo, SyncStatus, Peers, TransactionStats, ChainStatus}; #[test] fn test_serialize_sync_info() { let t = SyncInfo::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"startingBlock":"0x0","currentBlock":"0x0","highestBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null,"blockGap":null}"#); + assert_eq!(serialized, r#"{"startingBlock":"0x0","currentBlock":"0x0","highestBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null}"#); } #[test] @@ -190,16 +195,19 @@ mod tests { let t = SyncStatus::Info(SyncInfo::default()); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"startingBlock":"0x0","currentBlock":"0x0","highestBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null,"blockGap":null}"#); + assert_eq!(serialized, r#"{"startingBlock":"0x0","currentBlock":"0x0","highestBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null}"#); } #[test] fn test_serialize_block_gap() { - let mut t = SyncInfo::default(); + let mut t = ChainStatus::default(); + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!(serialized, r#"{"blockGap":null}"#); + t.block_gap = Some((1.into(), 5.into())); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"startingBlock":"0x0","currentBlock":"0x0","highestBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null,"blockGap":["0x1","0x5"]}"#) + assert_eq!(serialized, r#"{"blockGap":["0x1","0x5"]}"#); } #[test]